1 files copied
2 files deleted
36 files added
1 files renamed
80 files modified
4419 ■■■■ changed files
oying-system/src/main/java/com/oying/modules/message/domain/MessageOrderBuyer.java 7 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/domain/MessageOrderLeave.java 7 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/domain/MessageOrderSeller.java 7 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/domain/MessageSystem.java 3 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/domain/MessageSystemAdvertise.java 61 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/domain/dto/MessageSystemAdvertiseQueryCriteria.java 8 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/domain/dto/SendMessageRequestDto.java 14 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/mapper/MessageSystemAdvertiseMapper.java 22 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/rest/MessageOrderBuyerController.java 31 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/rest/MessageOrderLeaveController.java 30 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/rest/MessageOrderSellerController.java 24 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/rest/MessageSystemAdvertiseController.java 78 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/rest/MessageSystemController.java 96 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/service/MessageSystemAdvertiseService.java 60 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/service/impl/MessageSystemAdvertiseServiceImpl.java 81 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/converter/PlatformCategoryAssembler.java 28 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/converter/PlatformCategoryViewAssembler.java 2 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/domain/dto/PlatformCategoryUpdateRequest.java 3 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/domain/enums/PlatformCategoryEnum.java 28 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/rest/PlatformCategoryAdminController.java 4 ●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/rest/PlatformCategoryCustomerController.java 3 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/service/PlatformCategoryService.java 8 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/category/service/impl/PlatformCategoryServiceImpl.java 62 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/common/ValueUpdate.java 37 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/converter/ProductImageAssembler.java 38 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/converter/ProductLabelAssembler.java 26 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/Product.java 28 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/ProductImage.java 52 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/ProductLabel.java 49 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductCustomerQueryCriteria.java 25 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductImageCreateRequest.java 9 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductImageQueryCriteria.java 30 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductImageUpdateRequest.java 16 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductLabelCreateRequest.java 10 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductLabelQueryCriteria.java 4 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductMerchantCreateRequest.java 14 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductMerchantUpdateRequest.java 25 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductQueryCriteria.java 4 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/enums/ProductStatusEnum.java 33 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductImageMapper.java 24 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductLabelMapper.java 22 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductController.java 128 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductCustomerController.java 49 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductMerchantController.java 110 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductAdminService.java 14 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductImageService.java 98 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductLabelService.java 86 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductMerchantService.java 6 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductAdminServiceImpl.java 115 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductImageServiceImpl.java 173 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductLabelServiceImpl.java 133 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantCreateServiceImpl.java 59 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java 149 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java 7 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductCustomerView.java 73 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductImageCustomerView.java 16 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductLabelCustomerView.java 16 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductMerchantSimpleView.java 69 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductSimpleView.java 76 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/search/domain/dto/NearbyStoreQueryCriteria.java 2 ●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/search/rest/StoreSearchController.java 8 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/converter/StoreAssembler.java 32 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/converter/StoreQualificationAssembler.java 32 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/Store.java 42 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreQualification.java 4 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreStatus.java 5 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreUser.java 39 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCategoryMerchantCreateRequest.java 2 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCategoryMerchantUpdateRequest.java 4 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCategoryQueryCriteria.java 18 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCategoryUpdateRequest.java 4 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCreateRequest.java 61 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCustomerDetailDto.java 68 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCustomerQueryCriteria.java 37 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreFieldUpdateRequest.java 24 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQualificationCreateRequest.java 7 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQualificationQueryCriteria.java 2 ●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQualificationUpdateRequest.java 10 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQueryCriteria.java 37 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreUpdateRequest.java 103 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/enums/StoreCategoryEnum.java 28 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/enums/StoreStatusEnum.java 32 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreMapper.java 5 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreUserMapper.java 18 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCategoryController.java 71 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCategoryCustomerController.java 3 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCategoryMerchantController.java 6 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreController.java 64 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCustomerController.java 65 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreMerchantController.java 75 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreQualificationController.java 49 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreQualificationCustomerController.java 22 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreQualificationMerchantController.java 31 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreQualificationService.java 25 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreQueryService.java 7 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreService.java 34 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreCategoryServiceImpl.java 5 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreCreateServiceImpl.java 31 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQualificationServiceImpl.java 101 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQueryServiceImpl.java 63 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreServiceImpl.java 195 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/view/CustomerStoreQualificationView.java 10 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/view/CustomerStoreView.java 65 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/view/StoreMerchantView.java 24 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/view/StoreQualificationMerchantView.java 24 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/view/StoreSimpleView.java 71 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/utils/BusinessHoursUtils.java 42 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/utils/ImageUtils.java 31 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/message/MessageOrderBuyerMapper.xml 5 ●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/message/MessageOrderLeaveMapper.xml 5 ●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/message/MessageOrderSellerMapper.xml 5 ●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/message/MessageSystemAdvertiseMapper.xml 29 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/message/MessageSystemMapper.xml 6 ●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/product/ProductImageMapper.xml 30 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/product/ProductLabelMapper.xml 28 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/product/ProductMapper.xml 15 ●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/store/StoreCategoryMapper.xml 6 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/store/StoreMapper.xml 43 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/store/StoreQualificationMapper.xml 8 ●●●● patch | view | raw | blame | history
oying-tools/src/main/java/com/oying/utils/ObsUtils.java 11 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/message/domain/MessageOrderBuyer.java
@@ -1,5 +1,6 @@
package com.oying.modules.message.domain;
import com.oying.base.BaseEntity;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
@@ -19,7 +20,7 @@
**/
@Data
@TableName("message_order_buyer")
public class MessageOrderBuyer implements Serializable {
public class MessageOrderBuyer extends BaseEntity implements Serializable {
    @TableId(value = "id", type = IdType.AUTO)
    @ApiModelProperty(value = "主键,自增 用户id")
@@ -41,8 +42,8 @@
    @ApiModelProperty(value = "跳转链接")
    private String link;
    @ApiModelProperty(value = "创建时间")
    private Timestamp createTime;
//    @ApiModelProperty(value = "创建时间")
//    private Timestamp createTime;
    public void copy(MessageOrderBuyer source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
oying-system/src/main/java/com/oying/modules/message/domain/MessageOrderLeave.java
@@ -1,5 +1,6 @@
package com.oying.modules.message.domain;
import com.oying.base.BaseEntity;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
@@ -19,7 +20,7 @@
**/
@Data
@TableName("message_order_leave")
public class MessageOrderLeave implements Serializable {
public class MessageOrderLeave extends BaseEntity implements Serializable {
    @TableId(value = "id", type = IdType.AUTO)
    @ApiModelProperty(value = "主键,自增")
@@ -41,8 +42,8 @@
    @ApiModelProperty(value = "跳转链接")
    private String link="https://example.com/default-link";
    @ApiModelProperty(value = "创建时间")
    private Timestamp createTime;
//    @ApiModelProperty(value = "创建时间")
//    private Timestamp createTime;
    public void copy(MessageOrderLeave source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
oying-system/src/main/java/com/oying/modules/message/domain/MessageOrderSeller.java
@@ -1,5 +1,6 @@
package com.oying.modules.message.domain;
import com.oying.base.BaseEntity;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
@@ -19,7 +20,7 @@
**/
@Data
@TableName("message_order_seller")
public class MessageOrderSeller implements Serializable {
public class MessageOrderSeller extends BaseEntity implements Serializable {
    @TableId(value = "id", type = IdType.AUTO)
    @ApiModelProperty(value = "id")
@@ -41,8 +42,8 @@
    @ApiModelProperty(value = "link")
    private String link;
    @ApiModelProperty(value = "createTime")
    private Timestamp createTime;
//    @ApiModelProperty(value = "createTime")
//    private Timestamp createTime;
    public void copy(MessageOrderSeller source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
oying-system/src/main/java/com/oying/modules/message/domain/MessageSystem.java
@@ -1,5 +1,6 @@
package com.oying.modules.message.domain;
import com.oying.base.BaseEntity;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
@@ -21,7 +22,7 @@
 **/
@Data
@TableName("message_system")
public class MessageSystem implements Serializable {
public class MessageSystem extends BaseEntity implements Serializable {
    //使用数据库的自增策略来生成id
    @TableId(value = "id", type = IdType.AUTO)
oying-system/src/main/java/com/oying/modules/message/domain/MessageSystemAdvertise.java
New file
@@ -0,0 +1,61 @@
package com.oying.modules.message.domain;
import com.oying.base.BaseEntity;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import java.sql.Timestamp;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @description /
* @author leomon
* @date 2025-06-05
**/
@Data
@TableName("message_system_advertise")
public class MessageSystemAdvertise extends BaseEntity implements Serializable {
    @TableId(value = "id", type = IdType.AUTO)
    @ApiModelProperty(value = "主键ID")
    private Long id;
    @ApiModelProperty(value = "广告标题")
    private String title;
    @ApiModelProperty(value = "广告内容")
    private String content;
    @ApiModelProperty(value = "广告图片链接")
    private String imageUrl;
    @ApiModelProperty(value = "广告跳转链接")
    private String linkUrl;
    @NotNull
    @ApiModelProperty(value = "广告状态(0: 禁用;1: 启用)")
    private Integer status;
//    @ApiModelProperty(value = "创建人")
//    private String createBy;
//
//    @ApiModelProperty(value = "更新人")
//    private String updateBy;
//
//    @NotNull
//    @ApiModelProperty(value = "创建时间")
//    private Timestamp createTime;
//
//    @NotNull
//    @ApiModelProperty(value = "更新时间")
//    private Timestamp updateTime;
    public void copy(MessageSystemAdvertise source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
}
oying-system/src/main/java/com/oying/modules/message/domain/dto/MessageSystemAdvertiseQueryCriteria.java
copy from oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductCategoryQueryCriteria.java copy to oying-system/src/main/java/com/oying/modules/message/domain/dto/MessageSystemAdvertiseQueryCriteria.java
File was copied from oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductCategoryQueryCriteria.java
@@ -1,14 +1,14 @@
package com.oying.modules.pc.product.domain.dto;
package com.oying.modules.message.domain.dto;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
/**
* @author lzp
* @date 2025-05-13
* @author leomon
* @date 2025-06-05
**/
@Data
public class ProductCategoryQueryCriteria{
public class MessageSystemAdvertiseQueryCriteria{
    @ApiModelProperty(value = "页码", example = "1")
    private Integer page = 1;
oying-system/src/main/java/com/oying/modules/message/domain/dto/SendMessageRequestDto.java
New file
@@ -0,0 +1,14 @@
package com.oying.modules.message.domain.dto;
import lombok.Data;
import java.util.Map;
@Data
public class SendMessageRequestDto {
    private String touser;
    private String template_id;
    private String page;
    private Map<String, Object> data;
}
oying-system/src/main/java/com/oying/modules/message/mapper/MessageSystemAdvertiseMapper.java
New file
@@ -0,0 +1,22 @@
package com.oying.modules.message.mapper;
import com.oying.modules.message.domain.MessageSystemAdvertise;
import com.oying.modules.message.domain.dto.MessageSystemAdvertiseQueryCriteria;
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 leomon
* @date 2025-06-05
**/
@Mapper
public interface MessageSystemAdvertiseMapper extends BaseMapper<MessageSystemAdvertise> {
    IPage<MessageSystemAdvertise> findAll(@Param("criteria") MessageSystemAdvertiseQueryCriteria criteria, Page<Object> page);
    List<MessageSystemAdvertise> findAll(@Param("criteria") MessageSystemAdvertiseQueryCriteria criteria);
}
oying-system/src/main/java/com/oying/modules/message/rest/MessageOrderBuyerController.java
@@ -4,6 +4,7 @@
import com.oying.modules.message.domain.MessageOrderBuyer;
import com.oying.modules.message.service.MessageOrderBuyerService;
import com.oying.modules.message.domain.dto.MessageOrderBuyerQueryCriteria;
import com.oying.utils.R;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.http.HttpStatus;
@@ -21,6 +22,7 @@
* @author 李萌
* @date 2025-05-20
**/
//todo
@RestController
@RequiredArgsConstructor
@Api(tags = "买家端订单消息通知")
@@ -39,9 +41,11 @@
    @GetMapping
    @ApiOperation("查询买家端订单消息通知")
    @PreAuthorize("@el.check('messageOrderBuyer:list')")
    public ResponseEntity<PageResult<MessageOrderBuyer>> queryMessageOrderBuyer(MessageOrderBuyerQueryCriteria criteria){
    public R<PageResult<MessageOrderBuyer>> queryMessageOrderBuyer(MessageOrderBuyerQueryCriteria criteria){
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return new ResponseEntity<>(messageOrderBuyerService.queryAll(criteria,page),HttpStatus.OK);
//        return new ResponseEntity<>(messageOrderBuyerService.queryAll(criteria,page),HttpStatus.OK);
        return R.success(messageOrderBuyerService.queryAll(criteria,page));
    }
//    @PostMapping
@@ -73,34 +77,41 @@
    //订单状态变化通知
    @GetMapping("/status/{order_id}")
    @ApiOperation("查询一条订单状态变化通知")
    public ResponseEntity<String> getMessageOrderBuyer(@PathVariable Integer order_id) {
    public R<String> getMessageOrderBuyer(@PathVariable Integer order_id) {
        MessageOrderBuyer messageOrderBuyer = messageOrderBuyerService.findByOrderId(order_id);
        String message = messageOrderBuyer.getMessageType();
        if (messageOrderBuyer != null) {
            String message = messageOrderBuyer.getMessageType();
            return R.success(message);
        } else {
            return R.fail("订单状态变化通知不存在");
        }
        return new ResponseEntity<>(message, HttpStatus.OK);
    }
    //订单送达通知
    @GetMapping("/deliver/{order_id}")
    @ApiOperation("查询一条订单送达通知")
    public ResponseEntity<String> getMessageOrderBuyerDeliver(@PathVariable Integer order_id) {
    public R<String> getMessageOrderBuyerDeliver(@PathVariable Integer order_id) {
        MessageOrderBuyer messageOrderBuyer = messageOrderBuyerService.findByOrderId(order_id);
        String message = messageOrderBuyer.getMessageType();
        //如果MessageType为订单送达,则返回message——content
        if (message.equals("订单送达")) {
            String messageContent = messageOrderBuyer.getMessageContent();
            return new ResponseEntity<>(messageContent, HttpStatus.OK);
            return R.success(messageContent);
//            return new ResponseEntity<>(messageContent, HttpStatus.OK);
        }else{
            //返回没送达
            return new ResponseEntity<>("没送达", HttpStatus.OK);
            return R.fail("没送达");
//            return new ResponseEntity<>("没送达", HttpStatus.OK);
        }
    }
    //实现点击跳转到订单详情
    @GetMapping("/link/{order_id}")
    @ApiOperation("点击跳转到订单详情")
    public ResponseEntity<String> getMessageOrderBuyerLink(@PathVariable Integer order_id) {
    public R<String> getMessageOrderBuyerLink(@PathVariable Integer order_id) {
        MessageOrderBuyer messageOrderBuyer = messageOrderBuyerService.findByOrderId(order_id);
        String link = messageOrderBuyer.getLink();
        return new ResponseEntity<>(link, HttpStatus.OK);
//        return new ResponseEntity<>(link, HttpStatus.OK);
        return R.success(link);
    }
oying-system/src/main/java/com/oying/modules/message/rest/MessageOrderLeaveController.java
@@ -6,6 +6,7 @@
import com.oying.modules.message.domain.MessageOrderLeave;
import com.oying.modules.message.service.MessageOrderLeaveService;
import com.oying.modules.message.domain.dto.MessageOrderLeaveQueryCriteria;
import com.oying.utils.R;
import lombok.RequiredArgsConstructor;
import java.sql.Timestamp;
@@ -43,10 +44,12 @@
    @GetMapping
    @ApiOperation("查询消息留言")
    @PreAuthorize("@el.check('messageOrderLeave:list')")
    public ResponseEntity<PageResult<MessageOrderLeave>> queryMessageOrderLeave(MessageOrderLeaveQueryCriteria criteria){
    public R<PageResult<MessageOrderLeave>> queryMessageOrderLeave(MessageOrderLeaveQueryCriteria criteria){
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return new ResponseEntity<>(messageOrderLeaveService.queryAll(criteria,page),HttpStatus.OK);
//        return new ResponseEntity<>(messageOrderLeaveService.queryAll(criteria,page),HttpStatus.OK);
        return R.success(messageOrderLeaveService.queryAll(criteria,page));
    }
//    @PostMapping
//    @Log("新增消息留言")
@@ -100,13 +103,14 @@
    @PutMapping("/customer/{order_id}")
    @ApiOperation("顾客点击订单,传回订单id,根据订单id更新一条留言")
    public ResponseEntity<Object> updateMessageOrderLeaveCustomer(@PathVariable("order_id") Integer id, @RequestBody MessageOrderLeave messageOrderLeave1) {
    public R<Object> updateMessageOrderLeaveCustomer(@PathVariable("order_id") Integer id, @RequestBody MessageOrderLeave messageOrderLeave1) {
        // 根据 order_id 查找留言信息
        MessageOrderLeave messageOrderLeave = messageOrderLeaveService.findByOrderId(id);
        // 如果留言信息不存在,返回 404 Not Found
        if (messageOrderLeave == null) {
            return new ResponseEntity<>("Message order leave not found for order ID: " + id, HttpStatus.NOT_FOUND);
//            return new ResponseEntity<>("Message order leave not found for order ID: " + id, HttpStatus.NOT_FOUND);
            return R.fail("Message order leave not found for order ID: " + id);
        }
        // 设置顾客评价内容
@@ -124,10 +128,12 @@
        // 如果更新失败,返回 500 Internal Server Error
        if (!updated) {
            return new ResponseEntity<>("Failed to update message order leave", HttpStatus.INTERNAL_SERVER_ERROR);
//            return new ResponseEntity<>("Failed to update message order leave", HttpStatus.INTERNAL_SERVER_ERROR);
                return R.fail("Failed to update message order leave");
        }
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
//        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        return R.success(messageOrderLeave);
    }
    //商家对订单id进行回复
//    @PutMapping("/seller/{order_id}")
@@ -164,14 +170,16 @@
    //商家对订单id进行回复 订单id必须唯一限制 否则会报错
    @PutMapping("/seller/{order_id}")
    @ApiOperation("商家对订单id进行回复")
    public ResponseEntity<Object> updateMessageOrderLeaveSeller(@PathVariable("order_id") Integer id, @RequestBody MessageOrderLeave messageOrderLeave1) {
    public R<Object> updateMessageOrderLeaveSeller(@PathVariable("order_id") Integer id, @RequestBody MessageOrderLeave messageOrderLeave1) {
        // 根据 order_id 查找留言信息
        MessageOrderLeave messageOrderLeave = messageOrderLeaveService.findByOrderId(id);
        // 如果没有找到对应的留言信息,返回 404 Not Found
        if (messageOrderLeave == null) {
            return new ResponseEntity<>("Message order leave not found for order ID: " + id, HttpStatus.NOT_FOUND);
//            return new ResponseEntity<>("Message order leave not found for order ID: " + id, HttpStatus.NOT_FOUND);
                return R.fail("Message order leave not found for order ID: " + id);
        }
        // 设置商家回复内容
        messageOrderLeave.setSellerReply(messageOrderLeave1.getSellerReply());
@@ -183,10 +191,12 @@
        // 如果更新失败,返回 500 Internal Server Error
        if (!updated) {
            return new ResponseEntity<>("Failed to update message order leave", HttpStatus.INTERNAL_SERVER_ERROR);
//            return new ResponseEntity<>("Failed to update message order leave", HttpStatus.INTERNAL_SERVER_ERROR);
            return R.fail("Failed to update message order leave");
        }
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
//        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        return R.success(messageOrderLeave);
    }
}
oying-system/src/main/java/com/oying/modules/message/rest/MessageOrderSellerController.java
@@ -5,6 +5,7 @@
import com.oying.modules.message.domain.MessageOrderSeller;
import com.oying.modules.message.service.MessageOrderSellerService;
import com.oying.modules.message.domain.dto.MessageOrderSellerQueryCriteria;
import com.oying.utils.R;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.http.HttpStatus;
@@ -40,9 +41,10 @@
    @GetMapping
    @ApiOperation("查询卖家端订单消息通知")
    @PreAuthorize("@el.check('messageOrderSeller:list')")
    public ResponseEntity<PageResult<MessageOrderSeller>> queryMessageOrderSeller(MessageOrderSellerQueryCriteria criteria){
    public R<PageResult<MessageOrderSeller>> queryMessageOrderSeller(MessageOrderSellerQueryCriteria criteria){
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return new ResponseEntity<>(messageOrderSellerService.queryAll(criteria,page),HttpStatus.OK);
//        return new ResponseEntity<>(messageOrderSellerService.queryAll(criteria,page),HttpStatus.OK);
        return R.success(messageOrderSellerService.queryAll(criteria,page));
    }
//    @PostMapping
@@ -75,34 +77,38 @@
    //订单状态变化通知
    @GetMapping("/status/{order_id}")
    @ApiOperation("查询一条订单状态变化通知")
    public ResponseEntity<String> getMessageOrderSeller(@PathVariable Integer order_id) {
    public R<String> getMessageOrderSeller(@PathVariable Integer order_id) {
        MessageOrderSeller messageOrderSeller = messageOrderSellerService.findByOrderId(order_id);
        String message = messageOrderSeller.getMessageType();
        return new ResponseEntity<>(message, HttpStatus.OK);
//        return new ResponseEntity<>(message, HttpStatus.OK);
        return R.success(message);
    }
    //订单送达通知
    @GetMapping("/deliver/{order_id}")
    @ApiOperation("查询一条订单送达通知")
    public ResponseEntity<String> getMessageOrderSellerDeliver(@PathVariable Integer order_id) {
    public R<String> getMessageOrderSellerDeliver(@PathVariable Integer order_id) {
        MessageOrderSeller messageOrderSeller = messageOrderSellerService.findByOrderId(order_id);
        String message = messageOrderSeller.getMessageType();
        //如果MessageType为订单送达,则返回message——content
        if (message.equals("买家下单")) {
            String messageContent = messageOrderSeller.getMessageContent();
            return new ResponseEntity<>(messageContent, HttpStatus.OK);
//            return new ResponseEntity<>(messageContent, HttpStatus.OK);
            return R.success(messageContent);
        }else{
            //返回没送达
            return new ResponseEntity<>("买家没下单", HttpStatus.OK);
//            return new ResponseEntity<>("买家没下单", HttpStatus.OK);
            return R.fail("没送达");
        }
    }
    //实现点击跳转到订单详情
    @GetMapping("/link/{order_id}")
    @ApiOperation("点击跳转到订单详情")
    public ResponseEntity<String> getMessageOrderSellerLink(@PathVariable Integer order_id) {
    public R<String> getMessageOrderSellerLink(@PathVariable Integer order_id) {
        MessageOrderSeller messageOrderSeller = messageOrderSellerService.findByOrderId(order_id);
        String link = messageOrderSeller.getLink();
        return new ResponseEntity<>(link, HttpStatus.OK);
//        return new ResponseEntity<>(link, HttpStatus.OK);
        return R.success(link);
    }
}
oying-system/src/main/java/com/oying/modules/message/rest/MessageSystemAdvertiseController.java
New file
@@ -0,0 +1,78 @@
package com.oying.modules.message.rest;
import com.oying.annotation.Log;
import com.oying.modules.message.domain.MessageSystemAdvertise;
import com.oying.modules.message.service.MessageSystemAdvertiseService;
import com.oying.modules.message.domain.dto.MessageSystemAdvertiseQueryCriteria;
import com.oying.utils.R;
import lombok.RequiredArgsConstructor;
import java.util.List;
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 io.swagger.annotations.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.utils.PageResult;
/**
* @author leomon
* @date 2025-06-05
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "广告")
@RequestMapping("/api/messageSystemAdvertise")
public class MessageSystemAdvertiseController {
    private final MessageSystemAdvertiseService messageSystemAdvertiseService;
    @GetMapping
    @ApiOperation("页查询广告")
    @PreAuthorize("@el.check('messageSystemAdvertise:list')")
    public R<PageResult<MessageSystemAdvertise>> queryMessageSystemAdvertise(MessageSystemAdvertiseQueryCriteria criteria){
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
//        return new ResponseEntity<>(messageSystemAdvertiseService.queryAll(criteria,page),HttpStatus.OK);
        return R.success(messageSystemAdvertiseService.queryAll(criteria,page));
    }
    //条查询广告
    @GetMapping("/{id}")
    @ApiOperation("条查询广告")
    public R<MessageSystemAdvertise> getMessageSystemAdvertise(@PathVariable Integer id){
        MessageSystemAdvertise messageSystemAdvertise = messageSystemAdvertiseService.getById(id);
//        return new ResponseEntity<>(messageSystemAdvertise,HttpStatus.OK);
        return R.success(messageSystemAdvertise);
    }
//    @PostMapping
//    @Log("新增广告")
//    @ApiOperation("新增广告")
//    @PreAuthorize("@el.check('messageSystemAdvertise:add')")
//    public ResponseEntity<Object> createMessageSystemAdvertise(@Validated @RequestBody MessageSystemAdvertise resources){
//        messageSystemAdvertiseService.create(resources);
//        return new ResponseEntity<>(HttpStatus.CREATED);
//    }
//    @PutMapping
//    @Log("修改广告")
//    @ApiOperation("修改广告")
//    @PreAuthorize("@el.check('messageSystemAdvertise:edit')")
//    public ResponseEntity<Object> updateMessageSystemAdvertise(@Validated @RequestBody MessageSystemAdvertise resources){
//        messageSystemAdvertiseService.update(resources);
//        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
//    }
//    @DeleteMapping
//    @Log("删除广告")
//    @ApiOperation("删除广告")
//    @PreAuthorize("@el.check('messageSystemAdvertise:del')")
//    public ResponseEntity<Object> deleteMessageSystemAdvertise(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
//        messageSystemAdvertiseService.deleteAll(ids);
//        return new ResponseEntity<>(HttpStatus.OK);
//    }
}
oying-system/src/main/java/com/oying/modules/message/rest/MessageSystemController.java
@@ -1,16 +1,25 @@
package com.oying.modules.message.rest;
import com.alibaba.fastjson2.JSONObject;
import com.oying.annotation.Log;
import com.oying.modules.message.domain.MessageSystem;
import com.oying.modules.message.domain.dto.SendMessageRequestDto;
import com.oying.modules.message.service.MessageSystemService;
import com.oying.modules.message.domain.dto.MessageSystemQueryCriteria;
import com.oying.modules.system.domain.User;
import com.oying.modules.system.domain.UserSubscribe;
import com.oying.utils.HttpRequest;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -18,10 +27,12 @@
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.*;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.utils.PageResult;
import com.oying.modules.security.service.WeiXinService;
import com.oying.utils.R;
/**
 * @author 李萌
 * @date 2025-05-20
@@ -32,7 +43,11 @@
@RequestMapping("/api/message/messageSystem")
public class MessageSystemController {
    private static final Logger log = LoggerFactory.getLogger(MessageSystemController.class);
    private final MessageSystemService messageSystemService;
    private final WeiXinService weiXinService;
//    @ApiOperation("导出数据")
//    @GetMapping(value = "/download")
@@ -44,9 +59,10 @@
    @GetMapping
    @ApiOperation("查询消息_系统")
    @PreAuthorize("@el.check('messageSystem:list')")
    public ResponseEntity<PageResult<MessageSystem>> queryMessageSystem(MessageSystemQueryCriteria criteria){
    public R<PageResult<MessageSystem>> queryMessageSystem(MessageSystemQueryCriteria criteria){
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return new ResponseEntity<>(messageSystemService.queryAll(criteria,page),HttpStatus.OK);
//        return new ResponseEntity<>(messageSystemService.queryAll(criteria,page),HttpStatus.OK);
        return R.success(messageSystemService.queryAll(criteria,page));
    }
//    @PostMapping
@@ -79,14 +95,15 @@
    //查询一条系统消息
    @GetMapping("/{id}")
    @ApiOperation("查询一条系统消息")
    public ResponseEntity<MessageSystem> getMessageSystem(@PathVariable Integer id){
    public R<MessageSystem> getMessageSystem(@PathVariable Integer id){
        MessageSystem messageSystem = messageSystemService.getById(id);
        return new ResponseEntity<>(messageSystem,HttpStatus.OK);
//        return new ResponseEntity<>(messageSystem,HttpStatus.OK);
        return R.success(messageSystem);
    }
    //插入一条系统消息 带两个参数start与end
    @PostMapping()
    @ApiOperation("插入一条系统消息")
    public ResponseEntity<Object> insertMessageSystem(
    public R<Object> insertMessageSystem(
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date start,
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date end) {
@@ -98,8 +115,73 @@
        // 调用服务层保存数据
        messageSystemService.save(messageSystem);
        return new ResponseEntity<>(HttpStatus.OK);
//        return new ResponseEntity<>(HttpStatus.OK);
        return R.success("插入成功");
    }
    /**
     * POST 该接口用于发送订阅消息。
     *  data       请求参数
     * openid     用户openId
     * templateId 订阅模板id
     * page       小程序跳转链接
     */
//    public void sendMessage(Map<String, Object> data, String openid, String templateId, String page) {
//        JSONObject jsonObject = new JSONObject();
//        if (wxEnabled) {
//            String url = weiXinProperties.getSendMessage();
//            url = url.replace("{accessToken}", getStableAccessToken());
//            Map<String, Object> map = getSendMessageDto(data, openid, templateId, page);
//            jsonObject = HttpRequest.exchangeJsonObject(HttpMethod.POST, url, map);
//        } else {
//            jsonObject.put("message", "测试环境");
//        }
//        User user = userService.findByOpenid(openid);
//        UserSubscribe sub = new UserSubscribe();
//        sub.setSubType(templateId);
//        sub.setUserId(user.getId());
//        sub.setOpenid(openid);
//        sub.setUsername(user.getUsername());
//        sub.setSendMessage(JSONObject.toJSONString(data));
//        sub.setSubMessage(jsonObject.toJSONString());
//        subscribeService.save(sub);
//    }
    //调用微信发送消息接口sendMessage
    //示例
//    {
//        "touser": "OPENID",
//            "template_id": "TEMPLATE_ID",
//            "page": "index",
//            "data":{
//        "phrase3": {
//            "value": "审核通过"
//        },
//        "name1": {
//            "value": "订阅"
//        },
//        "date2": {
//            "value": "2019-12-25 09:42"
//        }
//    }
//    }
//todo  发送订阅消息   数据库暂未存储
    @PostMapping("/sendMessage")
    @ApiOperation("发送订阅消息")
    public R<SendMessageRequestDto> sendMessage(@RequestBody SendMessageRequestDto request){
        //打印request
        //SendMessageRequestDto(touser=OPENID, template_id=TEMPLATE_ID, page=index, data={date2={"value":"2019-12-25 09:42"}, phrase3={"value":"审核通过"}, name1={"value":"订阅"}})
        log.info("request:{}", request);
        try {
            weiXinService.sendMessage(request.getData(), request.getTouser(), request.getTemplate_id(), request.getPage());
            return R.success(request);
        } catch (Exception e) {
            // 如果捕获到异常,返回错误信息
            log.error("发送订阅消息失败", e);
            return R.fail(request,"发送订阅消息失败 用真实数据:" + e.getMessage());
        }
    }
}
oying-system/src/main/java/com/oying/modules/message/service/MessageSystemAdvertiseService.java
New file
@@ -0,0 +1,60 @@
package com.oying.modules.message.service;
import com.oying.modules.message.domain.MessageSystemAdvertise;
import com.oying.modules.message.domain.dto.MessageSystemAdvertiseQueryCriteria;
import java.util.Map;
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 leomon
* @date 2025-06-05
**/
public interface MessageSystemAdvertiseService extends IService<MessageSystemAdvertise> {
    /**
    * 查询数据分页
    * @param criteria 条件
    * @param page 分页参数
    * @return PageResult
    */
    PageResult<MessageSystemAdvertise> queryAll(MessageSystemAdvertiseQueryCriteria criteria, Page<Object> page);
    /**
    * 查询所有数据不分页
    * @param criteria 条件参数
    * @return List<MessageSystemAdvertiseDto>
    */
    List<MessageSystemAdvertise> queryAll(MessageSystemAdvertiseQueryCriteria criteria);
    /**
    * 创建
    * @param resources /
    */
    void create(MessageSystemAdvertise resources);
    /**
    * 编辑
    * @param resources /
    */
    void update(MessageSystemAdvertise resources);
    /**
    * 多选删除
    * @param ids /
    */
    void deleteAll(List<Long> ids);
    /**
    * 导出数据
    * @param all 待导出的数据
    * @param response /
    * @throws IOException /
    */
    void download(List<MessageSystemAdvertise> all, HttpServletResponse response) throws IOException;
}
oying-system/src/main/java/com/oying/modules/message/service/impl/MessageSystemAdvertiseServiceImpl.java
New file
@@ -0,0 +1,81 @@
package com.oying.modules.message.service.impl;
import com.oying.modules.message.domain.MessageSystemAdvertise;
import com.oying.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oying.modules.message.service.MessageSystemAdvertiseService;
import com.oying.modules.message.domain.dto.MessageSystemAdvertiseQueryCriteria;
import com.oying.modules.message.mapper.MessageSystemAdvertiseMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oying.utils.PageUtil;
import java.util.List;
import java.util.Map;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import com.oying.utils.PageResult;
/**
* @description 服务实现
* @author leomon
* @date 2025-06-05
**/
@Service
@RequiredArgsConstructor
public class MessageSystemAdvertiseServiceImpl extends ServiceImpl<MessageSystemAdvertiseMapper, MessageSystemAdvertise> implements MessageSystemAdvertiseService {
    private final MessageSystemAdvertiseMapper messageSystemAdvertiseMapper;
    @Override
    public PageResult<MessageSystemAdvertise> queryAll(MessageSystemAdvertiseQueryCriteria criteria, Page<Object> page){
        return PageUtil.toPage(messageSystemAdvertiseMapper.findAll(criteria, page));
    }
    @Override
    public List<MessageSystemAdvertise> queryAll(MessageSystemAdvertiseQueryCriteria criteria){
        return messageSystemAdvertiseMapper.findAll(criteria);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(MessageSystemAdvertise resources) {
        messageSystemAdvertiseMapper.insert(resources);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(MessageSystemAdvertise resources) {
        MessageSystemAdvertise messageSystemAdvertise = getById(resources.getId());
        messageSystemAdvertise.copy(resources);
        messageSystemAdvertiseMapper.updateById(messageSystemAdvertise);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        messageSystemAdvertiseMapper.deleteBatchIds(ids);
    }
    @Override
    public void download(List<MessageSystemAdvertise> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (MessageSystemAdvertise messageSystemAdvertise : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("广告标题", messageSystemAdvertise.getTitle());
            map.put("广告内容", messageSystemAdvertise.getContent());
            map.put("广告图片链接", messageSystemAdvertise.getImageUrl());
            map.put("广告跳转链接", messageSystemAdvertise.getLinkUrl());
            map.put("广告状态(0: 禁用;1: 启用)", messageSystemAdvertise.getStatus());
            map.put("创建人", messageSystemAdvertise.getCreateBy());
            map.put("更新人", messageSystemAdvertise.getUpdateBy());
            map.put("创建时间", messageSystemAdvertise.getCreateTime());
            map.put("更新时间", messageSystemAdvertise.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
oying-system/src/main/java/com/oying/modules/pc/category/converter/PlatformCategoryAssembler.java
New file
@@ -0,0 +1,28 @@
package com.oying.modules.pc.category.converter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.oying.modules.pc.category.domain.PlatformCategory;
import com.oying.modules.pc.category.domain.dto.PlatformCategoryCreateRequest;
import com.oying.modules.pc.category.domain.dto.PlatformCategoryUpdateDto;
import com.oying.modules.pc.utils.ImageUtils;
public class PlatformCategoryAssembler {
    public static PlatformCategory to(PlatformCategoryCreateRequest request) {
        PlatformCategory platformCategory = new PlatformCategory();
        BeanUtil.copyProperties(request, platformCategory, CopyOptions.create().setIgnoreNullValue(true));
        platformCategory.setIconId(request.getIconUploadFileId());
        platformCategory.setIconUrl(ImageUtils.getPublicObjectUrl(request.getIconUploadFileId()));
        return platformCategory;
    }
    public static PlatformCategory to(PlatformCategoryUpdateDto updateDto) {
        PlatformCategory platformCategory = new PlatformCategory();
        BeanUtil.copyProperties(updateDto, platformCategory, CopyOptions.create().setIgnoreNullValue(true));
        platformCategory.setIconId(updateDto.getIconUploadFileId());
        platformCategory.setIconUrl(ImageUtils.getPublicObjectUrl(updateDto.getIconUploadFileId()));
        return platformCategory;
    }
}
oying-system/src/main/java/com/oying/modules/pc/category/converter/PlatformCategoryViewAssembler.java
@@ -12,14 +12,12 @@
    public PlatformCategoryAdminView toPlatformCategoryResponse(PlatformCategory category) {
        PlatformCategoryAdminView categoryView = new PlatformCategoryAdminView();
        BeanUtil.copyProperties(category, categoryView);
        categoryView.setIconUrl("");
        return categoryView;
    }
    public PlatformCategoryCustomerView toCustomerPlatformCategoryResponse(PlatformCategory category) {
        PlatformCategoryCustomerView categoryView = new PlatformCategoryCustomerView();
        BeanUtil.copyProperties(category, categoryView);
        categoryView.setIconUrl("");
        return categoryView;
    }
}
oying-system/src/main/java/com/oying/modules/pc/category/domain/dto/PlatformCategoryUpdateRequest.java
@@ -9,6 +9,9 @@
@Data
public class PlatformCategoryUpdateRequest {
    @ApiModelProperty(value = "类目ID")
    private Long categoryId;
    @ApiModelProperty(value = "类目名称", example = "超市")
    private String name;
oying-system/src/main/java/com/oying/modules/pc/category/domain/enums/PlatformCategoryEnum.java
New file
@@ -0,0 +1,28 @@
package com.oying.modules.pc.category.domain.enums;
import lombok.Getter;
@Getter
public enum PlatformCategoryEnum {
    NOT(0, "已禁用"),
    YES(1, "已启用");
    private final Integer value;
    private final String reasonPhrase;
    PlatformCategoryEnum(int value, String reasonPhrase) {
        this.value = value;
        this.reasonPhrase = reasonPhrase;
    }
    public static PlatformCategoryEnum get(Integer code) {
        for (PlatformCategoryEnum value : values()) {
            if (value.value.equals(code)) {
                return value;
            }
        }
        return null;
    }
}
oying-system/src/main/java/com/oying/modules/pc/category/rest/PlatformCategoryAdminController.java
@@ -1,5 +1,6 @@
package com.oying.modules.pc.category.rest;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.annotation.Log;
import com.oying.modules.pc.category.converter.PlatformCategoryViewAssembler;
@@ -16,6 +17,7 @@
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -65,9 +67,9 @@
    //@PreAuthorize("@el.check('admin:platformCategory:updateById')")
    public ResponseEntity<?> updateById(@PathVariable Long categoryId,
                       @Validated @RequestBody PlatformCategoryUpdateRequest request) {
        request.setCategoryId(ObjectUtils.defaultIfNull(request.getCategoryId(), categoryId));
        PlatformCategoryUpdateDto updateDto = new PlatformCategoryUpdateDto();
        BeanUtils.copyProperties(request, updateDto);
        updateDto.setCategoryId(categoryId);
        categoryService.update(updateDto);
        return ResponseEntity.noContent().build();
    }
oying-system/src/main/java/com/oying/modules/pc/category/rest/PlatformCategoryCustomerController.java
@@ -2,6 +2,7 @@
import com.oying.modules.pc.category.converter.PlatformCategoryViewAssembler;
import com.oying.modules.pc.category.domain.PlatformCategory;
import com.oying.modules.pc.category.domain.enums.PlatformCategoryEnum;
import com.oying.modules.pc.category.view.PlatformCategoryCustomerView;
import com.oying.modules.pc.category.domain.dto.PlatformCategoryQueryCriteria;
import com.oying.modules.pc.category.service.PlatformCategoryService;
@@ -35,7 +36,7 @@
    //@PreAuthorize("@el.check('customer:platformCategory:list')")
    public ResponseEntity<?> queryPlatformCategory() {
        PlatformCategoryQueryCriteria criteria = new PlatformCategoryQueryCriteria();
        criteria.setActive(1);
        criteria.setActive(PlatformCategoryEnum.YES.getValue());
        List<PlatformCategory> platformCategoryList = categoryService.queryAll(criteria);
        List<PlatformCategoryCustomerView> responseList = platformCategoryList.stream().map(assembler::toCustomerPlatformCategoryResponse).collect(Collectors.toList());
        return ResponseEntity.ok(R.success(responseList));
oying-system/src/main/java/com/oying/modules/pc/category/service/PlatformCategoryService.java
@@ -37,6 +37,14 @@
    List<PlatformCategory> queryAll(PlatformCategoryQueryCriteria criteria);
    /**
     * 查询
     *
     * @param ids /
     * @return List<PlatformCategory>
     */
    List<PlatformCategory> queryBatchIds(List<Long> ids);
    /**
     * 创建
     *
     * @param resources /
oying-system/src/main/java/com/oying/modules/pc/category/service/impl/PlatformCategoryServiceImpl.java
@@ -1,26 +1,35 @@
package com.oying.modules.pc.category.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 cn.hutool.core.util.ObjUtil;
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.EntityExistException;
import com.oying.modules.pc.category.converter.PlatformCategoryDtoAssembler;
import com.oying.exception.EntityNotFoundException;
import com.oying.modules.pc.category.converter.PlatformCategoryAssembler;
import com.oying.modules.pc.category.domain.PlatformCategory;
import com.oying.modules.pc.category.domain.dto.PlatformCategoryCreateRequest;
import com.oying.modules.pc.category.domain.dto.PlatformCategoryQueryCriteria;
import com.oying.modules.pc.category.domain.dto.PlatformCategoryUpdateDto;
import com.oying.modules.pc.category.domain.dto.PlatformCategoryUpdateRequest;
import com.oying.modules.pc.category.mapper.PlatformCategoryMapper;
import com.oying.modules.pc.category.service.PlatformCategoryService;
import com.oying.modules.pc.common.ValueUpdate;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.utils.ImageUtils;
import com.oying.service.BucketStorageService;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import com.oying.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
 * 平台类目服务实现
@@ -33,6 +42,7 @@
public class PlatformCategoryServiceImpl extends ServiceImpl<PlatformCategoryMapper, PlatformCategory> implements PlatformCategoryService {
    private final PlatformCategoryMapper platformCategoryMapper;
    private final BucketStorageService bucketStorageService;
    @Override
    public PageResult<PlatformCategory> queryAll(PlatformCategoryQueryCriteria criteria, Page<Object> page) {
@@ -45,17 +55,16 @@
    }
    @Override
    public List<PlatformCategory> queryBatchIds(List<Long> ids) {
        LambdaQueryWrapper<PlatformCategory> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(PlatformCategory::getCategoryId, ids);
        return platformCategoryMapper.selectList(wrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(PlatformCategoryCreateRequest request) {
        PlatformCategory platformCategoryCreate = new PlatformCategory();
        platformCategoryCreate.setName(request.getName());
        platformCategoryCreate.setSortWeight(request.getSortWeight());
        platformCategoryCreate.setIconId(request.getIconUploadFileId());
        platformCategoryCreate.setActive(request.getActive());
        // 使用处理图标文件
        platformCategoryMapper.insert(platformCategoryCreate);
        platformCategoryMapper.insert(PlatformCategoryAssembler.to(request));
    }
    @Override
@@ -65,24 +74,35 @@
        Long categoryId = updateDto.getCategoryId();
        PlatformCategory existingPlatformCategory = this.getById(categoryId);
        if (ObjUtil.isEmpty(existingPlatformCategory)) {
            throw new EntityExistException(PlatformCategory.class, "categoryId", Optional.ofNullable(categoryId).map(Object::toString).orElse("null"));
            throw new EntityNotFoundException(PlatformCategory.class, "categoryId", Optional.ofNullable(categoryId).map(Object::toString).orElse("null"));
        }
        PlatformCategory platformCategoryUpdate = new PlatformCategory();
        platformCategoryUpdate.setCategoryId(updateDto.getCategoryId());
        platformCategoryUpdate.setName(updateDto.getName());
        platformCategoryUpdate.setSortWeight(updateDto.getSortWeight());
        platformCategoryUpdate.setIconId(updateDto.getIconUploadFileId());
        platformCategoryUpdate.setActive(updateDto.getActive());
        existingPlatformCategory.copy(platformCategoryUpdate);
        // 新的类目数据
        PlatformCategory newPlatformCategory = PlatformCategoryAssembler.to(updateDto);
        // 记录图片值的变更
        ValueUpdate<Long> iconValueUpdate = new ValueUpdate<>(newPlatformCategory.getIconId(), existingPlatformCategory.getIconId());
        // 填充新的数据
        existingPlatformCategory.copy(newPlatformCategory);
        platformCategoryMapper.updateById(existingPlatformCategory);
        // 删除旧图片原纪录
        if (iconValueUpdate.isChangeAndOldValueNotEmpty()) {
            bucketStorageService.deleteAll(ListUtil.toList(iconValueUpdate.getOldValue()));
        }
        // 使用处理图标文件
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        List<PlatformCategory> existingPlatformCategories = this.queryBatchIds(ids);
        if (CollectionUtil.isEmpty(existingPlatformCategories)) {
            throw new EntityNotFoundException(PlatformCategory.class, "platformCategoryIds", ids.toString());
        }
        platformCategoryMapper.deleteBatchIds(ids);
        List<Long> storageIds = existingPlatformCategories.stream().map(PlatformCategory::getIconId).collect(Collectors.toList());
        // 删除旧图片原记录
        if (CollectionUtil.isNotEmpty(storageIds)) {
            bucketStorageService.deleteAll(storageIds);
        }
    }
}
oying-system/src/main/java/com/oying/modules/pc/common/ValueUpdate.java
New file
@@ -0,0 +1,37 @@
package com.oying.modules.pc.common;
import cn.hutool.core.util.ObjUtil;
import lombok.Data;
/**
 * 用于封装字段值的变化记录,支持泛型比较逻辑
 * @param <T>
 */
@Data
public class ValueUpdate<T> {
    private final T newValue;
    private final T oldValue;
    public ValueUpdate(T newValue, T oldValue) {
        this.newValue = newValue;
        this.oldValue = oldValue;
    }
    public boolean isChange() {
        return isChange(newValue, oldValue);
    }
    public boolean isChangeAndOldValueNotEmpty() {
        return isChangeAndOldValueNotEmpty(newValue, oldValue);
    }
    public static <T> boolean isChange(T newValue, T oldValue) {
        return ObjUtil.isNotEmpty(newValue) && !newValue.equals(oldValue);
    }
    public static <T> boolean isChangeAndOldValueNotEmpty(T newValue, T oldValue) {
        return ObjUtil.isNotEmpty(oldValue) && isChange(newValue, oldValue);
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/converter/ProductImageAssembler.java
New file
@@ -0,0 +1,38 @@
package com.oying.modules.pc.product.converter;
import cn.hutool.core.collection.ListUtil;
import com.oying.modules.pc.product.domain.ProductImage;
import com.oying.modules.pc.product.domain.dto.ProductImageCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductImageUpdateRequest;
import com.oying.modules.pc.utils.ImageUtils;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class ProductImageAssembler {
    public static ProductImage to(ProductImageCreateRequest request) {
        ProductImage productImage = new ProductImage();
        productImage.setProductId(request.getProductId());
        productImage.setImageType(Optional.ofNullable(request.getImageType()).orElse("main"));
        productImage.setCloudStorageId(request.getUploadFileId());
        productImage.setImageUrl(ImageUtils.getPublicObjectUrl(request.getUploadFileId()));
        return productImage;
    }
    public static List<ProductImage> toProducts(List<ProductImageCreateRequest> requests) {
        return Optional.ofNullable(requests).orElse(ListUtil.empty()).stream().map(ProductImageAssembler::to).collect(Collectors.toList());
    }
    public static ProductImage to(ProductImageUpdateRequest request) {
        ProductImage productImage = new ProductImage();
        productImage.setImageId(request.getImageId());
        productImage.setProductId(request.getProductId());
        productImage.setImageType(request.getImageType());
        productImage.setCloudStorageId(request.getUploadFileId());
        productImage.setImageUrl(ImageUtils.getPublicObjectUrl(request.getUploadFileId()));
        return productImage;
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/converter/ProductLabelAssembler.java
New file
@@ -0,0 +1,26 @@
package com.oying.modules.pc.product.converter;
import cn.hutool.core.collection.ListUtil;
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.ProductImageUpdateRequest;
import com.oying.modules.pc.product.domain.dto.ProductLabelCreateRequest;
import com.oying.modules.pc.utils.ImageUtils;
import java.util.List;
import java.util.Optional;
public class ProductLabelAssembler {
    public static ProductLabel to(ProductLabelCreateRequest request) {
        ProductLabel productLabel = new ProductLabel();
        productLabel.setProductId(request.getProductId());
        productLabel.setCategoryName(request.getCategoryName());
        productLabel.setLabelName(request.getLabelName());
        productLabel.setLabelValue(request.getLabelValue());
        productLabel.setUnit(request.getUnit());
        return productLabel;
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/Product.java
@@ -2,6 +2,8 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.oying.base.BaseEntity;
@@ -13,6 +15,7 @@
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* @author lzp
@@ -23,7 +26,7 @@
@TableName("pc_product")
public class Product extends BaseEntity implements Serializable {
    @TableId(value = "product_id")
    @TableId(value = "product_id", type = IdType.AUTO)
    @ApiModelProperty(value = "ID")
    private Long productId;
@@ -52,15 +55,16 @@
    private Long secondCategoryId;
    @NotNull
    @ApiModelProperty(value = "状态:1000-草稿 1001上架 1002下架")
    @ApiModelProperty(value = "状态")
    private Integer status;
    @NotBlank
    @ApiModelProperty(value = "主图片")
    private String mainImage;
    private String mainImageId;
    @ApiModelProperty(value = "详情图片")
    private String detailImage;
    @NotBlank
    @ApiModelProperty(value = "主图地址")
    private String mainImageUrl;
    @ApiModelProperty(value = "商品描述")
    private String description;
@@ -92,6 +96,12 @@
    @ApiModelProperty(value = "高度(单位:厘米)")
    private Integer height;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @NotNull
    @ApiModelProperty(value = "是否删除")
    private Integer deletedFlag;
@@ -100,6 +110,14 @@
    @ApiModelProperty(value = "版本号")
    private Long version;
    @TableField(exist = false)
    @ApiModelProperty(value = "图片")
    private List<ProductImage> images;
    @TableField(exist = false)
    @ApiModelProperty(value = "标签")
    private List<ProductLabel> labels;
    public void copy(Product source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
oying-system/src/main/java/com/oying/modules/pc/product/domain/ProductImage.java
New file
@@ -0,0 +1,52 @@
package com.oying.modules.pc.product.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.oying.base.BaseEntity;
import lombok.Getter;
import lombok.Setter;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import java.sql.Timestamp;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @description /
* @author lzp
* @date 2025-05-28
**/
@Getter
@Setter
@TableName("pc_product_image")
public class ProductImage extends BaseEntity implements Serializable {
    @TableId(value = "image_id", type = IdType.AUTO)
    @ApiModelProperty(value = "商品图片ID")
    private Long imageId;
    @NotNull
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @NotNull
    @ApiModelProperty(value = "桶ID")
    private Long cloudStorageId;
    @NotBlank
    @ApiModelProperty(value = "图片类型")
    private String imageType;
    @ApiModelProperty(value = "图片地址")
    private String imageUrl;
    @ApiModelProperty(value = "排序权重")
    private Integer sortWeight;
    public void copy(ProductImage source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/ProductLabel.java
New file
@@ -0,0 +1,49 @@
package com.oying.modules.pc.product.domain;
import com.oying.base.BaseEntity;
import lombok.Getter;
import lombok.Setter;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import java.sql.Timestamp;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @description /
* @author lzp
* @date 2025-05-28
**/
@Getter
@Setter
@TableName("pc_product_label")
public class ProductLabel extends BaseEntity implements Serializable {
    @TableId(value = "label_id", type = IdType.AUTO)
    @ApiModelProperty(value = "标签ID")
    private Long labelId;
    @NotNull
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @ApiModelProperty(value = "分类名称")
    private String categoryName;
    @ApiModelProperty(value = "标签名称")
    private String labelName;
    @ApiModelProperty(value = "标签值")
    private String labelValue;
    @ApiModelProperty(value = "单位")
    private String unit;
    public void copy(ProductLabel source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductCustomerQueryCriteria.java
@@ -1,7 +1,13 @@
package com.oying.modules.pc.product.domain.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
 * @author lzp
@@ -10,8 +16,27 @@
@Data
public class ProductCustomerQueryCriteria {
    @ApiModelProperty(value = "搜索字段", example = "柚子")
    private String blurry;
    @ApiModelProperty(value = "ID")
    private Long productId;
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "一级分类ID")
    private Long categoryId;
    @ApiModelProperty(value = "二级分类ID")
    private Long secondCategoryId;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "是否删除")
    private Integer deletedFlag;
    @ApiModelProperty(value = "页码", example = "1")
    private Integer page = 1;
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductImageCreateRequest.java
@@ -1,9 +1,14 @@
package com.oying.modules.pc.product.domain.dto;
import lombok.Data;
@Data
public class ProductImageCreateRequest {
    private Long uploadId;
    private Long productId;
    private Integer type;
    private Long uploadFileId;
    private String imageType;
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductImageQueryCriteria.java
New file
@@ -0,0 +1,30 @@
package com.oying.modules.pc.product.domain.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author lzp
* @date 2025-05-28
**/
@Data
public class ProductImageQueryCriteria{
    @ApiModelProperty(value = "商品图片ID")
    private Long imageId;
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @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/ProductImageUpdateRequest.java
New file
@@ -0,0 +1,16 @@
package com.oying.modules.pc.product.domain.dto;
import lombok.Data;
@Data
public class ProductImageUpdateRequest {
    private Long imageId;
    private Long productId;
    private Long uploadFileId;
    private String imageType;
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductLabelCreateRequest.java
@@ -1,16 +1,24 @@
package com.oying.modules.pc.product.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class ProductLabelCreateRequest {
    private String category;
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @ApiModelProperty(value = "分类名称")
    private String categoryName;
    @ApiModelProperty(value = "标签名称")
    private String labelName;
    @ApiModelProperty(value = "标签值")
    private String labelValue;
    @ApiModelProperty(value = "单位")
    private String unit;
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductLabelQueryCriteria.java
File was renamed from oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductCategoryQueryCriteria.java
@@ -5,10 +5,10 @@
/**
* @author lzp
* @date 2025-05-13
* @date 2025-05-28
**/
@Data
public class ProductCategoryQueryCriteria{
public class ProductLabelQueryCriteria{
    @ApiModelProperty(value = "页码", example = "1")
    private Integer page = 1;
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductMerchantCreateRequest.java
@@ -1,14 +1,18 @@
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;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Data
public class ProductMerchantCreateRequest {
    private Long storeId;
    @NotBlank
    private String barcode;
@@ -43,10 +47,14 @@
    private Integer height;
    private Integer allowReturns;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    private List<ProductImageCreateRequest> imageList;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    private List<ProductLabelCreateRequest> labelList;
    private List<ProductImageCreateRequest> images = new ArrayList<>();
    private List<ProductLabelCreateRequest> labels = new ArrayList<>();
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductMerchantUpdateRequest.java
@@ -1,14 +1,17 @@
package com.oying.modules.pc.product.domain.dto;
import com.oying.modules.pc.product.domain.ProductLabel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Data
public class ProductMerchantUpdateRequest {
    private Long productId;
    private String barcode;
@@ -36,10 +39,22 @@
    private Integer height;
    private Integer allowReturns;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    private List<ProductImageCreateRequest> imageList;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    private List<ProductLabelCreateRequest> specList;
    private List<Long> deletedImageIds = new ArrayList<>();
    private List<ProductImageUpdateRequest> updatedImages = new ArrayList<>();
    private List<ProductImageCreateRequest> newImages = new ArrayList<>();
    private List<Long> deletedLabelIds = new ArrayList<>();
    private List<ProductLabel> updatedLabels = new ArrayList<>();
    private List<ProductLabel> newLabels = new ArrayList<>();
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductQueryCriteria.java
@@ -17,6 +17,8 @@
    private Long storeId;
    private String barcode;
    private Integer status;
    private Long categoryId;
@@ -31,4 +33,6 @@
    @ApiModelProperty(value = "每页数据量", example = "10")
    private Integer size = 10;
    private Integer limit;
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/enums/ProductStatusEnum.java
New file
@@ -0,0 +1,33 @@
package com.oying.modules.pc.product.domain.enums;
import lombok.Getter;
@Getter
public enum ProductStatusEnum {
    DRAFT(1000, "创建中"),
    PENDING(1001, "待审核"),
    UNDER_REVIEW(1002, "审核中"),
    REJECTED(1003, "拒绝"),
    APPROVED(1004, "同意"),
    AVAILABLE(2100, "在售"),
    NO_AVAILABLE(2101, "停售"),
    BANNED(3000, "禁止售卖");
    private final Integer value;
    private final String reasonPhrase;
    ProductStatusEnum(int value, String reasonPhrase) {
        this.value = value;
        this.reasonPhrase = reasonPhrase;
    }
    public static ProductStatusEnum get(Integer code) {
        for (ProductStatusEnum value : values()) {
            if (value.value.equals(code)) {
                return value;
            }
        }
        return null;
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductImageMapper.java
New file
@@ -0,0 +1,24 @@
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.ProductImage;
import com.oying.modules.pc.product.domain.dto.ProductImageQueryCriteria;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author lzp
 * @date 2025-05-28
 **/
@Mapper
public interface ProductImageMapper extends BaseMapper<ProductImage> {
    IPage<ProductImage> findAll(@Param("criteria") ProductImageQueryCriteria criteria, Page<Object> page);
    List<ProductImage> findAll(@Param("criteria") ProductImageQueryCriteria criteria);
}
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductLabelMapper.java
New file
@@ -0,0 +1,22 @@
package com.oying.modules.pc.product.mapper;
import com.oying.modules.pc.product.domain.ProductLabel;
import com.oying.modules.pc.product.domain.dto.ProductLabelQueryCriteria;
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-05-28
**/
@Mapper
public interface ProductLabelMapper extends BaseMapper<ProductLabel> {
    IPage<ProductLabel> findAll(@Param("criteria") ProductLabelQueryCriteria criteria, Page<Object> page);
    List<ProductLabel> findAll(@Param("criteria") ProductLabelQueryCriteria criteria);
}
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductController.java
@@ -1,73 +1,141 @@
package com.oying.modules.pc.product.rest;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.annotation.Log;
import com.oying.utils.R;
import com.oying.modules.pc.product.domain.Product;
import com.oying.modules.pc.product.service.ProductService;
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.dto.ProductQueryCriteria;
import com.oying.modules.pc.product.service.ProductAdminService;
import com.oying.modules.pc.product.service.ProductImageService;
import com.oying.modules.pc.product.service.ProductLabelService;
import com.oying.modules.pc.product.service.ProductService;
import com.oying.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.utils.PageResult;
import java.io.IOException;
import java.util.List;
/**
* @author lzp
* @date 2025-04-30
**/
 * @author lzp
 * @date 2025-04-30
 **/
@RestController
@RequiredArgsConstructor
@Api(tags = "商品")
@RequestMapping("/api/product")
@RequestMapping("/api/pc/product")
public class ProductController {
    private final ProductService productService;
    private final ProductAdminService productAdminService;
    private final ProductImageService productImageService;
    private final ProductLabelService productLabelService;
    @ApiOperation("导出数据")
    @GetMapping(value = "/download")
    @PreAuthorize("@el.check('product:list')")
    // @PreAuthorize("@el.check('product:list')")
    public void exportProduct(HttpServletResponse response, ProductQueryCriteria criteria) throws IOException {
        productService.download(productService.queryAll(criteria), response);
    }
    @GetMapping
    @GetMapping(value = "/list")
    @ApiOperation("查询商品")
    @PreAuthorize("@el.check('product:list')")
    public R<PageResult<Product>> queryProduct(ProductQueryCriteria criteria){
    // @PreAuthorize("@el.check('product:list')")
    public ResponseEntity<?> getProducts(ProductQueryCriteria criteria) {
        return ResponseEntity.ok(R.success(productService.queryAll(criteria)));
    }
    @GetMapping(value = "/batch")
    @ApiOperation("查询商品(批量)")
    // @PreAuthorize("@el.check('product:list')")
    public ResponseEntity<?> getProductsByIds(@RequestBody List<Long> ids) {
        return ResponseEntity.ok(R.success(productService.listByIds(ids)));
    }
    @GetMapping(value = "/page")
    @ApiOperation("查询商品")
    // @PreAuthorize("@el.check('product:list')")
    public ResponseEntity<?> getProductsByPage(ProductQueryCriteria criteria) {
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return R.success(productService.queryAll(criteria,page));
        return ResponseEntity.ok(R.success(productService.queryAll(criteria, page)));
    }
    @GetMapping(value = "/{productId}}")
    @ApiOperation("查询商品")
    // @PreAuthorize("@el.check('product:list')")
    public ResponseEntity<?> getProduct(@PathVariable Long productId) {
        Product product = productService.getById(productId);
        if (ObjectUtil.isNotEmpty(product)) {
            product.setLabels(productLabelService.queryLabelsByProductId(productId));
        }
        return ResponseEntity.ok(R.success(product));
    }
    @GetMapping(value = "/{productId}}/details")
    @ApiOperation("查询商品")
    // @PreAuthorize("@el.check('product:list')")
    public ResponseEntity<?> getProductDetails(@PathVariable Long productId) {
        Product product = productService.getById(productId);
        if (ObjectUtil.isNotEmpty(product)) {
            product.setImages(productImageService.queryImagesByProductId(productId));
            product.setLabels(productLabelService.queryLabelsByProductId(productId));
        }
        return ResponseEntity.ok(R.success(product));
    }
    @GetMapping(value = "/{productId}}/images")
    @ApiOperation("查询商品")
    // @PreAuthorize("@el.check('product:list')")
    public ResponseEntity<?> getProductImages(@PathVariable Long productId) {
        return ResponseEntity.ok(R.success(productImageService.queryImagesByProductId(productId)));
    }
    @GetMapping(value = "/{productId}}/labels")
    @ApiOperation("查询商品")
    // @PreAuthorize("@el.check('product:list')")
    public ResponseEntity<?> getProductLabels(@PathVariable Long productId) {
        return ResponseEntity.ok(R.success(productLabelService.queryLabelsByProductId(productId)));
    }
    @PostMapping
    @Log("新增商品")
    @ApiOperation("新增商品")
    @PreAuthorize("@el.check('product:add')")
    public R<?> createProduct(@Validated @RequestBody Product resources){
        productService.create(resources);
        return R.success();
    //@PreAuthorize("@el.check('merchant:product:add')")
    public ResponseEntity<?> createProduct(@PathVariable Long storeId,
                                           @Validated @RequestBody ProductMerchantCreateRequest request) {
        request.setStoreId(ObjectUtils.defaultIfNull(request.getStoreId(), storeId));
        productAdminService.create(request);
        return ResponseEntity.noContent().build();
    }
    @PutMapping
    @Log("修改商品")
    @ApiOperation("修改商品")
    @PreAuthorize("@el.check('product:edit')")
    public R<?> updateProduct(@Validated @RequestBody Product resources){
        productService.update(resources);
        return R.success();
    // @PreAuthorize("@el.check('product:edit')")
    public ResponseEntity<?> updateProduct(@PathVariable Long productId,
                                           @Validated @RequestBody ProductMerchantUpdateRequest request) {
        request.setProductId(ObjectUtils.defaultIfNull(request.getProductId(), productId));
        productAdminService.update(request);
        return ResponseEntity.noContent().build();
    }
    @DeleteMapping
    @Log("删除商品")
    @ApiOperation("删除商品")
    @PreAuthorize("@el.check('product:del')")
    public R<?> deleteProduct(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
        productService.deleteAll(ids);
        return R.success();
    // @PreAuthorize("@el.check('product:del')")
    public ResponseEntity<?> deleteProduct(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
        productAdminService.delete(ids);
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductCustomerController.java
@@ -1,13 +1,20 @@
package com.oying.modules.pc.product.rest;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.utils.R;
import com.oying.modules.pc.product.domain.Product;
import com.oying.modules.pc.product.domain.dto.ProductQueryCriteria;
import com.oying.modules.pc.product.service.ProductImageService;
import com.oying.modules.pc.product.service.ProductLabelService;
import com.oying.modules.pc.product.service.ProductService;
import com.oying.modules.pc.product.view.ProductCustomerView;
import com.oying.modules.pc.product.view.ProductImageCustomerView;
import com.oying.modules.pc.product.view.ProductLabelCustomerView;
import com.oying.utils.PageResult;
import com.oying.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@@ -20,11 +27,11 @@
import java.util.stream.Collectors;
/*
*
 *
 * @author lzp
 * @date 2025-04-30
 *
*/
 */
@RestController
@RequiredArgsConstructor
@Api(tags = "商品(客户端)")
@@ -32,14 +39,16 @@
public class ProductCustomerController {
    private final ProductService productService;
    private final ProductImageService productImageService;
    private final ProductLabelService productLabelService;
    @GetMapping(value = "/page")
    @ApiOperation("根据商品名称模糊匹配店铺内的商品")
    /*@PreAuthorize("@el.check('merchant:product:page')")*/
    public ResponseEntity<?> query(@PathVariable Long storeId,
                                   @RequestParam(value = "categoryId", required = false) Long categoryId,
                                   @RequestParam(value = "secondCategoryId", required = false) Long secondCategoryId,
                                   @RequestParam(value = "blurry", required = false) String blurry) {
    public ResponseEntity<?> getProductsByPage(@PathVariable Long storeId,
                                               @RequestParam(value = "categoryId", required = false) Long categoryId,
                                               @RequestParam(value = "secondCategoryId", required = false) Long secondCategoryId,
                                               @RequestParam(value = "blurry", required = false) String blurry) {
        ProductQueryCriteria criteria = new ProductQueryCriteria();
        criteria.setStoreId(storeId);
@@ -54,9 +63,7 @@
        PageResult<ProductCustomerView> viewPageResult = new PageResult<>(
                productList.stream().map(i -> {
                    ProductCustomerView view = new ProductCustomerView();
                    BeanUtils.copyProperties(i, view);
                    view.setScore(5.0D);
                    view.setSold(0);
                    BeanUtil.copyProperties(i, view);
                    return view;
                }).collect(Collectors.toList()),
                productPageResult.getTotalElements());
@@ -66,16 +73,22 @@
    @GetMapping(value = "/{productId}/details")
    @ApiOperation("查询商品")
    /*@PreAuthorize("@el.check('merchant:product:byProductId')")*/
    public ResponseEntity<?> getDetails(@PathVariable Long productId) {
    public ResponseEntity<?> getProductDetails(@PathVariable Long productId) {
        Product product = productService.getById(productId);
        ProductCustomerView customerView = new ProductCustomerView();
        customerView.setProductId(product.getProductId());
        customerView.setTitle(product.getTitle());
        customerView.setPrice(product.getPrice());
        customerView.setScore(5.0D);
        customerView.setSold(0);
        customerView.setLabelList(ListUtil.empty());
        customerView.setMainImageList(ListUtil.empty());
        BeanUtil.copyProperties(product, customerView);
        if (ObjUtil.isNotEmpty(product)) {
            customerView.setImages(productImageService.queryImagesByProductId(productId).stream().map(i -> {
                ProductImageCustomerView imageCustomerView = new ProductImageCustomerView();
                BeanUtil.copyProperties(i, imageCustomerView, CopyOptions.create().setIgnoreNullValue(true));
                return imageCustomerView;
            }).collect(Collectors.toList()));
            customerView.setLabels(productLabelService.queryLabelsByProductId(productId).stream().map(i -> {
                ProductLabelCustomerView labelCustomerView = new ProductLabelCustomerView();
                BeanUtil.copyProperties(i, labelCustomerView, CopyOptions.create().setIgnoreNullValue(true));
                return labelCustomerView;
            }).collect(Collectors.toList()));
        }
        return ResponseEntity.ok(R.success(customerView));
    }
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductMerchantController.java
@@ -1,23 +1,29 @@
package com.oying.modules.pc.product.rest;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.annotation.Log;
import com.oying.utils.R;
import com.oying.modules.pc.product.domain.Product;
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.ProductMerchantCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductMerchantUpdateRequest;
import com.oying.modules.pc.product.domain.dto.ProductQueryCriteria;
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.view.ProductMerchantDetailsView;
import com.oying.modules.pc.product.view.ProductMerchantSimpleView;
import com.oying.utils.PageResult;
import com.oying.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -36,24 +42,27 @@
@RequestMapping("/api/pc/merchant/store/{storeId}/product")
public class ProductMerchantController {
    private final int MAX_IMAGES = 5;
    private final int MAX_LABELS = 10;
    private final ProductService productService;
    private final ProductMerchantService productMerchantService;
    private final ProductImageService productImageService;
    private final ProductLabelService productLabelService;
    @GetMapping(value = "/page")
    @ApiOperation("获取指定商户店铺的商品列表(支持分页)")
    /*@PreAuthorize("@el.check('merchant:product:page') " +
            "and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> query(@PathVariable Long storeId,
                                   ProductQueryCriteria criteria) {
        criteria.setStoreId(storeId);
    public ResponseEntity<?> getProductsByPage(@PathVariable Long storeId, ProductQueryCriteria criteria) {
        criteria.setStoreId(ObjectUtils.defaultIfNull(criteria.getStoreId(), storeId));
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        PageResult<Product> productPageResult = productService.queryAll(criteria, page);
        List<Product> productList = Optional.ofNullable(productPageResult.getContent()).orElse(ListUtil.empty());
        PageResult<ProductMerchantSimpleView> viewPageResult = new PageResult<>(
                productList.stream().map(i -> {
                    ProductMerchantSimpleView view = new ProductMerchantSimpleView();
                    BeanUtils.copyProperties(i, view);
                    BeanUtil.copyProperties(i, view);
                    return view;
                }).collect(Collectors.toList()),
                productPageResult.getTotalElements());
@@ -64,7 +73,7 @@
    @ApiOperation("查询商品")
    /*@PreAuthorize("@el.check('merchant:product:byProductId') " +
            "and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> getById(@PathVariable Long productId) {
    public ResponseEntity<?> getProductById(@PathVariable Long productId) {
        return ResponseEntity.ok(R.success(productService.getById(productId)));
    }
@@ -74,14 +83,11 @@
            "and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> getDetailsById(@PathVariable Long productId) {
        Product product = productService.getById(productId);
        ProductMerchantDetailsView view = null;
        if (product != null) {
            view = new ProductMerchantDetailsView();
            BeanUtils.copyProperties(product, view);
            view.setMainImageList(ListUtil.empty());
            view.setLabelList(ListUtil.empty());
        if (ObjUtil.isNotEmpty(product)) {
            product.setImages(productImageService.queryImagesByProductId(productId));
            product.setLabels(productLabelService.queryLabelsByProductId(productId));
        }
        return ResponseEntity.ok(R.success(view));
        return ResponseEntity.ok(R.success(product));
    }
    @PostMapping
@@ -91,20 +97,20 @@
    //        "and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> createProduct(@PathVariable Long storeId,
                                           @Validated @RequestBody ProductMerchantCreateRequest request) {
        productMerchantService.create(storeId, request);
        return ResponseEntity.noContent().build();
        request.setStoreId(ObjectUtils.defaultIfNull(request.getStoreId(), storeId));
        productMerchantService.create(request);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    @PutMapping(value = "/{productId}")
    @Log("修改商品")
    //@Log("修改商品")
    @ApiOperation("修改商品")
    /*@PreAuthorize("@el.check('merchant:product:edit') " +
            "and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateProduct(@PathVariable Long productId,
                                           @Validated @RequestBody ProductMerchantUpdateRequest request) {
        productMerchantService.update(productId, request);
        request.setProductId(ObjectUtils.defaultIfNull(request.getProductId(), productId));
        productMerchantService.update(request);
        return ResponseEntity.noContent().build();
    }
@@ -137,4 +143,64 @@
        productMerchantService.takeOffShelf(productId);
        return ResponseEntity.noContent().build();
    }
    @PostMapping(value = "/{productId}/images")
    @Log("添加商品图片")
    @ApiOperation("添加商品图片")
    public ResponseEntity<?> batchAddImage(@PathVariable("productId") Long productId,
                                           @RequestBody List<ProductImageCreateRequest> requests) {
        requests = requests.stream().peek(i -> i.setProductId(Optional.ofNullable(i.getProductId()).orElse(productId)))
                .collect(Collectors.toList());
        productImageService.batchCreate(requests);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    @PutMapping(value = "/{productId}/images/change")
    @Log("商品图片变更")
    @ApiOperation("商品图片变更")
    public ResponseEntity<?> updateImage(@PathVariable("productId") Long productId,
                                           @RequestBody ProductMerchantUpdateRequest request) {
        request.setProductId(ObjectUtils.defaultIfNull(request.getProductId(), productId));
        productMerchantService.updateImages(request);
        return ResponseEntity.noContent().build();
    }
    @DeleteMapping(value = "/{productId}/images")
    @Log("删除商品图片")
    @ApiOperation("添加商品图片")
    public ResponseEntity<?> deleteImage(@PathVariable("productId") Long productId,
                                         @RequestBody List<Long> ids) {
        productImageService.deleteAll(ids);
        return ResponseEntity.noContent().build();
    }
    @PostMapping(value = "/{productId}/labels")
    @Log("添加商品标签")
    @ApiOperation("添加商品标签")
    public ResponseEntity<?> batchAddLabel(@PathVariable("productId") Long productId,
                                           @RequestBody List<ProductLabel> requests) {
        requests = requests.stream().peek(i -> i.setProductId(Optional.ofNullable(i.getProductId()).orElse(productId)))
                .collect(Collectors.toList());
        productLabelService.batchCreate(requests);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    @PutMapping(value = "/{productId}/labels/change")
    @Log("标签变更")
    @ApiOperation("标签变更")
    public ResponseEntity<?> updateLabels(@PathVariable("productId") Long productId,
                                         @RequestBody ProductMerchantUpdateRequest request) {
        request.setProductId(ObjectUtils.defaultIfNull(request.getProductId(), productId));
        productMerchantService.updateLabels(request);
        return ResponseEntity.noContent().build();
    }
    @DeleteMapping(value = "/{productId}/labels")
    @Log("删除商品标签")
    @ApiOperation("添加商品标签")
    public ResponseEntity<?> deleteLabel(@PathVariable("productId") Long productId,
                                         @RequestBody List<Long> ids) {
        productLabelService.deleteAll(ids);
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductAdminService.java
New file
@@ -0,0 +1,14 @@
package com.oying.modules.pc.product.service;
import com.oying.modules.pc.product.domain.dto.ProductMerchantCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductMerchantUpdateRequest;
import java.util.List;
public interface ProductAdminService {
    void create(ProductMerchantCreateRequest request);
    void update(ProductMerchantUpdateRequest request);
    void delete(List<Long> ids);
}
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductImageService.java
New file
@@ -0,0 +1,98 @@
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.ProductImage;
import com.oying.modules.pc.product.domain.dto.ProductImageCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductImageQueryCriteria;
import com.oying.modules.pc.product.domain.dto.ProductImageUpdateRequest;
import com.oying.utils.PageResult;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
 * @author lzp
 * @description 服务接口
 * @date 2025-05-28
 **/
public interface ProductImageService extends IService<ProductImage> {
    /**
     * 查询数据分页
     *
     * @param criteria 条件
     * @param page     分页参数
     * @return PageResult
     */
    PageResult<ProductImage> queryAll(ProductImageQueryCriteria criteria, Page<Object> page);
    /**
     * 查询所有数据不分页
     *
     * @param criteria 条件参数
     * @return List<ProductImageDto>
     */
    List<ProductImage> queryAll(ProductImageQueryCriteria criteria);
    /**
     * 查询多条ID关联数据
     *
     * @param ids /
     * @return List<ProductImage>
     */
    List<ProductImage> queryBatchIds(List<Long> ids);
    /**
     * 查询商品的所有图片
     *
     * @param productId /
     * @return List<ProductImage>
     */
    List<ProductImage> queryImagesByProductId(Long productId);
    /**
     * 创建
     *
     * @param request /
     */
    void create(ProductImageCreateRequest request);
    /**
     * 批量创建
     *
     * @param requests /
     */
    void batchCreate(List<ProductImageCreateRequest> requests);
    /**
     * 编辑
     *
     * @param request /
     */
    void update(ProductImageUpdateRequest request);
    /**
     * 编辑
     *
     * @param requests /
     */
    void batchUpdate(List<ProductImageUpdateRequest> requests);
    /**
     * 多选删除
     *
     * @param ids /
     */
    void deleteAll(List<Long> ids);
    /**
     * 导出数据
     *
     * @param all      待导出的数据
     * @param response /
     * @throws IOException /
     */
    void download(List<ProductImage> all, HttpServletResponse response) throws IOException;
}
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductLabelService.java
New file
@@ -0,0 +1,86 @@
package com.oying.modules.pc.product.service;
import com.oying.modules.pc.product.domain.ProductLabel;
import com.oying.modules.pc.product.domain.dto.ProductLabelQueryCriteria;
import java.util.Map;
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-05-28
**/
public interface ProductLabelService extends IService<ProductLabel> {
    /**
    * 查询数据分页
    * @param criteria 条件
    * @param page 分页参数
    * @return PageResult
    */
    PageResult<ProductLabel> queryAll(ProductLabelQueryCriteria criteria, Page<Object> page);
    /**
    * 查询所有数据不分页
    * @param criteria 条件参数
    * @return List<ProductLabel>
    */
    List<ProductLabel> queryAll(ProductLabelQueryCriteria criteria);
    /**
     * 查询
     * @param ids /
     * @return List<ProductLabel>
     */
    List<ProductLabel> queryBatchIds(List<Long> ids);
    /**
     * 查询商品的所有标签
     * @param productId /
     * @return List<ProductLabel>
     */
    List<ProductLabel> queryLabelsByProductId(Long productId);
    /**
    * 创建
    * @param resources /
    */
    void create(ProductLabel resources);
    /**
     * 批量创建
     * @param resources /
     */
    void batchCreate(List<ProductLabel> resources);
    /**
    * 编辑
    * @param resources /
    */
    void update(ProductLabel resources);
    /**
     * 编辑
     * @param resources /
     */
    void batchUpdate(List<ProductLabel> resources);
    /**
    * 多选删除
    * @param ids /
    */
    void deleteAll(List<Long> ids);
    /**
    * 导出数据
    * @param all 待导出的数据
    * @param response /
    * @throws IOException /
    */
    void download(List<ProductLabel> all, HttpServletResponse response) throws IOException;
}
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductMerchantService.java
@@ -7,8 +7,10 @@
public interface ProductMerchantService {
    void create(Long storeId, ProductMerchantCreateRequest request);
    void update(Long storeId, ProductMerchantUpdateRequest request);
    void create(ProductMerchantCreateRequest request);
    void update(ProductMerchantUpdateRequest request);
    void updateImages(ProductMerchantUpdateRequest request);
    void updateLabels(ProductMerchantUpdateRequest request);
    void batchDelete(List<Long> ids);
    void putOnShelf(Long productId);
    void takeOffShelf(Long productId);
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductAdminServiceImpl.java
New file
@@ -0,0 +1,115 @@
package com.oying.modules.pc.product.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import com.oying.exception.EntityExistException;
import com.oying.exception.EntityNotFoundException;
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.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.ProductMerchantCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductMerchantUpdateRequest;
import com.oying.modules.pc.product.domain.enums.ProductStatusEnum;
import com.oying.modules.pc.product.service.ProductAdminService;
import com.oying.modules.pc.product.service.ProductImageService;
import com.oying.modules.pc.product.service.ProductLabelService;
import com.oying.modules.pc.product.service.ProductService;
import com.oying.modules.pc.utils.ImageUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ProductAdminServiceImpl implements ProductAdminService {
    private final ProductService productService;
    private final ProductImageService productImageService;
    private final ProductLabelService productLabelService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(ProductMerchantCreateRequest request) {
        Product product = new Product();
        BeanUtils.copyProperties(request, product);
        request.getImages().stream().findFirst().map(ProductImageCreateRequest::getUploadFileId)
                .ifPresent(id -> {
                    product.setMainImageId(id.toString());
                    product.setMainImageUrl(ImageUtils.getPublicObjectUrl(id));
                });
        product.setStatus(ProductStatusEnum.DRAFT.getValue());
        productService.create(product);
        List<ProductImage> productImages = request.getImages().stream().map(i -> {
            i.setProductId(product.getProductId());
            return ProductImageAssembler.to(i);
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(productImages)) {
            productImageService.saveBatch(productImages);
        }
        List<ProductLabel> productLabels = request.getLabels().stream().map(i -> {
            i.setProductId(product.getProductId());
            return ProductLabelAssembler.to(i);
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(productLabels)) {
            productLabelService.saveBatch(productLabels);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ProductMerchantUpdateRequest request) {
        Product existingProduct = this.findOrThrow(request.getProductId());
        this.processImagesUpdate(request);
        this.processLabelsUpdate(request);
        BeanUtils.copyProperties(request, existingProduct);
        productService.update(existingProduct);
    }
    @Transactional
    @Override
    public void delete(List<Long> ids) {
        productService.deleteAll(ids);
    }
    private Product findOrThrow(Long productId) {
        Product existingProduct = productService.getById(productId);
        if (ObjUtil.isEmpty(existingProduct)) {
            throw new EntityNotFoundException(Product.class, "id", Optional.ofNullable(productId).map(Object::toString).orElse("null"));
        }
        return existingProduct;
    }
    private void processImagesUpdate(ProductMerchantUpdateRequest request) {
        if (CollectionUtil.isNotEmpty(request.getDeletedImageIds())) {
            productImageService.deleteAll(request.getDeletedImageIds());
        }
        if (CollectionUtil.isNotEmpty(request.getUpdatedImages())) {
            productImageService.batchUpdate(request.getUpdatedImages());
        }
        if (CollectionUtil.isNotEmpty(request.getNewImages())) {
            productImageService.batchCreate(request.getNewImages());
        }
    }
    private void processLabelsUpdate(ProductMerchantUpdateRequest request) {
        if (CollectionUtil.isNotEmpty(request.getDeletedLabelIds())) {
            productLabelService.deleteAll(request.getDeletedLabelIds());
        }
        if (CollectionUtil.isNotEmpty(request.getUpdatedLabels())) {
            productLabelService.batchUpdate(request.getUpdatedLabels());
        }
        if (CollectionUtil.isNotEmpty(request.getNewLabels())) {
            productLabelService.batchCreate(request.getNewLabels());
        }
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductImageServiceImpl.java
New file
@@ -0,0 +1,173 @@
package com.oying.modules.pc.product.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.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oying.exception.EntityExistException;
import com.oying.exception.EntityNotFoundException;
import com.oying.modules.pc.common.ValueUpdate;
import com.oying.modules.pc.product.converter.ProductImageAssembler;
import com.oying.modules.pc.product.domain.ProductImage;
import com.oying.modules.pc.product.domain.dto.ProductImageCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductImageQueryCriteria;
import com.oying.modules.pc.product.domain.dto.ProductImageUpdateRequest;
import com.oying.modules.pc.product.mapper.ProductImageMapper;
import com.oying.modules.pc.product.service.ProductImageService;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.service.BucketStorageService;
import com.oying.utils.FileUtil;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author lzp
 * @description 服务实现
 * @date 2025-05-28
 **/
@Service
@RequiredArgsConstructor
public class ProductImageServiceImpl extends ServiceImpl<ProductImageMapper, ProductImage> implements ProductImageService {
    private final ProductImageMapper productImageMapper;
    private final BucketStorageService bucketStorageService;
    @Override
    public PageResult<ProductImage> queryAll(ProductImageQueryCriteria criteria, Page<Object> page) {
        return PageUtil.toPage(productImageMapper.findAll(criteria, page));
    }
    @Override
    public List<ProductImage> queryAll(ProductImageQueryCriteria criteria) {
        return productImageMapper.findAll(criteria);
    }
    @Override
    public List<ProductImage> queryBatchIds(List<Long> ids) {
        LambdaQueryWrapper<ProductImage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(ProductImage::getImageId, ids);
        return productImageMapper.selectList(lambdaQueryWrapper);
    }
    @Override
    public List<ProductImage> queryImagesByProductId(Long productId) {
        if (productId == null) {
            return ListUtil.empty();
        }
        LambdaQueryWrapper<ProductImage> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductImage::getProductId, productId);
        return productImageMapper.selectList(wrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(ProductImageCreateRequest request) {
        productImageMapper.insert(ProductImageAssembler.to(request));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchCreate(List<ProductImageCreateRequest> requests) {
        this.saveBatch(ProductImageAssembler.toProducts(requests));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ProductImageUpdateRequest request) {
        Long updatedImageId = request.getImageId();
        ProductImage existingProductImage = this.getById(updatedImageId);
        if (ObjUtil.isEmpty(existingProductImage)) {
            throw new EntityNotFoundException(ProductImage.class, "imageId", Optional.ofNullable(updatedImageId).map(Object::toString).orElse("null"));
        }
        // 新的图片数据
        ProductImage newProductImage = ProductImageAssembler.to(request);
        // 新的图片数据
        ValueUpdate<Long> cloudStorageUpdate = new ValueUpdate<>(newProductImage.getCloudStorageId(), existingProductImage.getCloudStorageId());
        // 填充新的数据
        existingProductImage.copy(newProductImage);
        productImageMapper.updateById(existingProductImage);
        // 删除旧图片原纪录
        if (cloudStorageUpdate.isChangeAndOldValueNotEmpty()) {
            bucketStorageService.deleteAll(ListUtil.toList(cloudStorageUpdate.getOldValue()));
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdate(List<ProductImageUpdateRequest> requests) {
        List<Long> updatedIds = requests.stream().map(ProductImageUpdateRequest::getImageId).collect(Collectors.toList());
        List<ProductImage> existingProductImages = this.queryBatchIds(updatedIds);
        if (CollectionUtil.isEmpty(existingProductImages)) {
            throw new EntityNotFoundException(ProductImage.class, "imageIds", updatedIds.toString());
        }
        Map<Long, ProductImageUpdateRequest> requestMap = requests.stream().collect(Collectors.toMap(
                                ProductImageUpdateRequest::getImageId,
                                productImageUpdateRequest -> productImageUpdateRequest
                        )
                );
        List<Long> deleteCloudStorageIds = existingProductImages.stream().filter(i -> {
            Long newCloudStorageId = requestMap.get(i.getImageId()).getUploadFileId();
            return ValueUpdate.isChangeAndOldValueNotEmpty(newCloudStorageId, i.getCloudStorageId());
        }).map(ProductImage::getImageId).collect(Collectors.toList());
        // 填充新的数据
        for (ProductImage existingProductImage : existingProductImages) {
            ProductImage newProductImage = ProductImageAssembler.to(requestMap.get(existingProductImage.getImageId()));
            existingProductImage.copy(newProductImage);
        }
        this.updateBatchById(existingProductImages);
        // 删除旧图片原纪录
        if (CollectionUtil.isNotEmpty(deleteCloudStorageIds)) {
            bucketStorageService.deleteAll(deleteCloudStorageIds);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        List<ProductImage> existingProductImages = this.queryBatchIds(ids);
        if (CollectionUtil.isEmpty(existingProductImages)) {
            throw new EntityNotFoundException(ProductImage.class, "productImageIds", ids.toString());
        }
        productImageMapper.deleteBatchIds(ids);
        // 删除图片原纪录
        List<Long> deleteCloudStorageIds = existingProductImages.stream().map(ProductImage::getCloudStorageId).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(deleteCloudStorageIds)) {
            bucketStorageService.deleteAll(deleteCloudStorageIds);
        }
    }
    @Override
    public void download(List<ProductImage> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (ProductImage productImage : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("商品ID", productImage.getProductId());
            map.put("桶ID", productImage.getCloudStorageId());
            map.put("图片key", productImage.getImageType());
            map.put("图片地址", productImage.getImageUrl());
            map.put("排序权重", productImage.getSortWeight());
            map.put("创建人", productImage.getCreateBy());
            map.put("创建日期", productImage.getCreateTime());
            map.put("修改人", productImage.getUpdateBy());
            map.put("修改时间", productImage.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductLabelServiceImpl.java
New file
@@ -0,0 +1,133 @@
package com.oying.modules.pc.product.service.impl;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
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.EntityExistException;
import com.oying.exception.EntityNotFoundException;
import com.oying.modules.pc.product.domain.ProductLabel;
import com.oying.modules.pc.product.domain.dto.ProductLabelQueryCriteria;
import com.oying.modules.pc.product.mapper.ProductLabelMapper;
import com.oying.modules.pc.product.service.ProductLabelService;
import com.oying.utils.FileUtil;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import lombok.RequiredArgsConstructor;
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;
import java.util.stream.Collectors;
/**
 * @author lzp
 * @description 服务实现
 * @date 2025-05-28
 **/
@Service
@RequiredArgsConstructor
public class ProductLabelServiceImpl extends ServiceImpl<ProductLabelMapper, ProductLabel> implements ProductLabelService {
    private final ProductLabelMapper productLabelMapper;
    @Override
    public PageResult<ProductLabel> queryAll(ProductLabelQueryCriteria criteria, Page<Object> page) {
        return PageUtil.toPage(productLabelMapper.findAll(criteria, page));
    }
    @Override
    public List<ProductLabel> queryAll(ProductLabelQueryCriteria criteria) {
        return productLabelMapper.findAll(criteria);
    }
    @Override
    public List<ProductLabel> queryBatchIds(List<Long> ids) {
        LambdaQueryWrapper<ProductLabel> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ProductLabel::getLabelId, ids);
        return productLabelMapper.selectList(wrapper);
    }
    @Override
    public List<ProductLabel> queryLabelsByProductId(Long productId) {
        if (productId == null) {
            return ListUtil.empty();
        }
        LambdaQueryWrapper<ProductLabel> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductLabel::getProductId, productId);
        return productLabelMapper.selectList(wrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(ProductLabel resources) {
        productLabelMapper.insert(resources);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchCreate(List<ProductLabel> resources) {
        this.saveBatch(resources);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ProductLabel resources) {
        ProductLabel productLabel = getById(resources.getLabelId());
        productLabel.copy(resources);
        productLabelMapper.updateById(productLabel);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdate(List<ProductLabel> resources) {
        List<Long> updateIds = resources.stream().map(ProductLabel::getLabelId).collect(Collectors.toList());
        List<ProductLabel> existingProductLabels = this.queryBatchIds(updateIds);
        if (ObjUtil.isEmpty(existingProductLabels)) {
            throw new EntityNotFoundException(ProductLabel.class, "labelId", updateIds.toString());
        }
        Map<Long, ProductLabel> longProductLabelMap = resources.stream()
                .collect(Collectors.toMap(
                        ProductLabel::getLabelId,
                        productLabel -> productLabel)
                );
        for (ProductLabel existingProductLabel : existingProductLabels) {
            existingProductLabel.copy(longProductLabelMap.get(existingProductLabel.getLabelId()));
        }
        this.updateBatchById(existingProductLabels);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        productLabelMapper.deleteBatchIds(ids);
    }
    @Override
    public void download(List<ProductLabel> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (ProductLabel productLabel : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("商品ID", productLabel.getProductId());
            map.put(" categoryName", productLabel.getCategoryName());
            map.put("标签名称", productLabel.getLabelName());
            map.put("标签值", productLabel.getLabelValue());
            map.put("创建人", productLabel.getCreateBy());
            map.put("创建时间", productLabel.getCreateTime());
            map.put("修改人", productLabel.getUpdateBy());
            map.put("修改时间", productLabel.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantCreateServiceImpl.java
File was deleted
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java
New file
@@ -0,0 +1,149 @@
package com.oying.modules.pc.product.service.impl;
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.EntityNotFoundException;
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.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.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.utils.ImageUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ProductMerchantServiceImpl implements ProductMerchantService {
    private final ProductService productService;
    private final ProductImageService productImageService;
    private final ProductLabelService productLabelService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(ProductMerchantCreateRequest request) {
        Product product = new Product();
        BeanUtils.copyProperties(request, product);
        request.getImages().stream().findFirst().map(ProductImageCreateRequest::getUploadFileId)
                .ifPresent(id -> {
                    product.setMainImageId(id.toString());
                    product.setMainImageUrl(ImageUtils.getPublicObjectUrl(id));
                });
        product.setStatus(ProductStatusEnum.DRAFT.getValue());
        productService.create(product);
        List<ProductImage> productImages = request.getImages().stream().map(i -> {
            i.setProductId(product.getProductId());
            return ProductImageAssembler.to(i);
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(productImages)) {
            productImageService.saveBatch(productImages);
        }
        List<ProductLabel> productLabels = request.getLabels().stream().map(i -> {
            i.setProductId(product.getProductId());
            return ProductLabelAssembler.to(i);
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(productLabels)) {
            productLabelService.saveBatch(productLabels);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ProductMerchantUpdateRequest request) {
        Product existingProduct = this.findOrThrow(request.getProductId());
        this.processImagesUpdate(request);
        this.processLabelsUpdate(request);
        BeanUtils.copyProperties(request, existingProduct);
        productService.update(existingProduct);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateImages(ProductMerchantUpdateRequest request) {
        this.findOrThrow(request.getProductId());
        this.processImagesUpdate(request);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLabels(ProductMerchantUpdateRequest request) {
        this.findOrThrow(request.getProductId());
        this.processImagesUpdate(request);
    }
    @Transactional
    @Override
    public void batchDelete(List<Long> ids) {
        productService.deleteAll(ids);
    }
    @Override
    public void putOnShelf(Long productId) {
        LambdaUpdateWrapper<Product> wrapper = new LambdaUpdateWrapper<Product>()
                .eq(Product::getProductId, productId)
                .set(Product::getStatus, ProductStatusEnum.AVAILABLE.getValue());
        productService.update(wrapper);
    }
    @Override
    public void takeOffShelf(Long productId) {
        LambdaUpdateWrapper<Product> wrapper = new LambdaUpdateWrapper<Product>()
                .eq(Product::getProductId, productId)
                .set(Product::getStatus, ProductStatusEnum.NO_AVAILABLE.getValue());
        productService.update(wrapper);
    }
    private Product findOrThrow(Long productId) {
        Product existingProduct = productService.getById(productId);
        if (ObjUtil.isEmpty(existingProduct)) {
            throw new EntityNotFoundException(Product.class, "id", Optional.ofNullable(productId).map(Object::toString).orElse("null"));
        }
        return existingProduct;
    }
    private void processImagesUpdate(ProductMerchantUpdateRequest request) {
        if (CollectionUtil.isNotEmpty(request.getDeletedImageIds())) {
            productImageService.deleteAll(request.getDeletedImageIds());
        }
        if (CollectionUtil.isNotEmpty(request.getUpdatedImages())) {
            productImageService.batchUpdate(request.getUpdatedImages());
        }
        if (CollectionUtil.isNotEmpty(request.getNewImages())) {
            List<ProductImageCreateRequest> newImages = request.getNewImages().stream().peek(i-> i.setProductId(request.getProductId())).collect(Collectors.toList());
            productImageService.batchCreate(newImages);
        }
    }
    private void processLabelsUpdate(ProductMerchantUpdateRequest request) {
        if (CollectionUtil.isNotEmpty(request.getDeletedLabelIds())) {
            productLabelService.deleteAll(request.getDeletedLabelIds());
        }
        if (CollectionUtil.isNotEmpty(request.getUpdatedLabels())) {
            productLabelService.batchUpdate(request.getUpdatedLabels());
        }
        if (CollectionUtil.isNotEmpty(request.getNewLabels())) {
            List<ProductLabel> newLabels = request.getNewLabels().stream().peek(i-> i.setProductId(request.getProductId())).collect(Collectors.toList());
            productLabelService.batchCreate(newLabels);
        }
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductServiceImpl.java
@@ -10,7 +10,6 @@
import com.oying.utils.FileUtil;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import com.oying.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -79,9 +78,9 @@
            map.put("商品名称", product.getName());
            map.put("商品标题", product.getTitle());
            map.put("分类ID", product.getCategoryId());
            map.put("状态:1000-草稿 1001上架 1002下架", product.getStatus());
            map.put("主图片", product.getMainImage());
            map.put("详情图片", product.getDetailImage());
            map.put("状态", product.getStatus());
            map.put("主图片", product.getMainImageId());
            map.put("主图地址", product.getMainImageUrl());
            map.put("商品描述", product.getDescription());
            map.put("销售价格", product.getPrice());
            map.put("库存数量", product.getStockQuantity());
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductCustomerView.java
@@ -1,8 +1,14 @@
package com.oying.modules.pc.product.view;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
@@ -13,13 +19,66 @@
@Data
public class ProductCustomerView {
    private Long productId;
    private String title;
    private BigDecimal price;
    private Double score;           // 评分
    private Integer sold;    // 月销量
    //private Integer sold;    // 月销量
    private List<ProductImageCustomerView> mainImageList;
    private List<ProductLabelCustomerView> labelList;
    @ApiModelProperty(value = "ID")
    private Long productId;
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "条形码")
    private String barcode;
    @ApiModelProperty(value = "商品名称")
    private String name;
    @ApiModelProperty(value = "商品标题")
    private String title;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "主图地址")
    private String mainImageUrl;
    @ApiModelProperty(value = "商品描述")
    private String description;
    @ApiModelProperty(value = "销售价格")
    private BigDecimal price;
    @ApiModelProperty(value = "库存数量")
    private Integer stockQuantity;
    @ApiModelProperty(value = "起售数量")
    private Integer minPurchaseQuantity;
    @ApiModelProperty(value = "重量(单位:g)")
    private Integer weight;
    @ApiModelProperty(value = "宽度(单位:厘米)")
    private Integer width;
    @ApiModelProperty(value = "长度(单位:厘米)")
    private Integer length;
    @ApiModelProperty(value = "高度(单位:厘米)")
    private Integer height;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "版本号")
    private Long version;
    @ApiModelProperty(value = "月销售量")
    private Integer monthlySales = 0;
    private List<ProductImageCustomerView> images = new ArrayList<>();
    private List<ProductLabelCustomerView> labels = new ArrayList<>();
}
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductImageCustomerView.java
@@ -1,12 +1,24 @@
package com.oying.modules.pc.product.view;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class ProductImageCustomerView {
    private String url;
    private Integer primaryFlag;
    @ApiModelProperty(value = "商品图片ID")
    private Long imageId;
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @ApiModelProperty(value = "图片类型")
    private String imageType;
    @ApiModelProperty(value = "图片地址")
    private String imageUrl;
    @ApiModelProperty(value = "排序权重")
    private Integer sortWeight;
}
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductLabelCustomerView.java
@@ -1,13 +1,27 @@
package com.oying.modules.pc.product.view;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class ProductLabelCustomerView {
    private Integer categoryId;
    @ApiModelProperty(value = "标签ID")
    private Long labelId;
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @ApiModelProperty(value = "分类名称")
    private String categoryName;
    @ApiModelProperty(value = "标签名称")
    private String labelName;
    @ApiModelProperty(value = "标签值")
    private String labelValue;
    @ApiModelProperty(value = "单位")
    private String unit;
}
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductMerchantSimpleView.java
@@ -1,5 +1,6 @@
package com.oying.modules.pc.product.view;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@@ -12,11 +13,73 @@
@Data
public class ProductMerchantSimpleView {
    @ApiModelProperty(value = "ID")
    private Long productId;
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "条形码")
    private String barcode;
    @ApiModelProperty(value = "商品名称")
    private String name;
    private BigDecimal price;
    private Integer stockQuantity;
    private Integer sold;
    @ApiModelProperty(value = "商品标题")
    private String title;
    @ApiModelProperty(value = "一级分类ID")
    private Long categoryId;
    @ApiModelProperty(value = "二级分类ID")
    private Long secondCategoryId;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "主图片")
    private String mainImageId;
    @ApiModelProperty(value = "主图地址")
    private String mainImageUrl;
    @ApiModelProperty(value = "商品描述")
    private String description;
    @ApiModelProperty(value = "销售价格")
    private BigDecimal price;
    @ApiModelProperty(value = "库存数量")
    private Integer stockQuantity;
    @ApiModelProperty(value = "起售数量")
    private Integer minPurchaseQuantity;
    @ApiModelProperty(value = "预警库存")
    private Integer warnStock;
    @ApiModelProperty(value = "重量(单位:g)")
    private Integer weight;
    @ApiModelProperty(value = "宽度(单位:厘米)")
    private Integer width;
    @ApiModelProperty(value = "长度(单位:厘米)")
    private Integer length;
    @ApiModelProperty(value = "高度(单位:厘米)")
    private Integer height;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "是否删除")
    private Integer deletedFlag;
    @ApiModelProperty(value = "版本号")
    private Long version;
}
oying-system/src/main/java/com/oying/modules/pc/product/view/ProductSimpleView.java
@@ -1,5 +1,8 @@
package com.oying.modules.pc.product.view;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
@@ -7,15 +10,76 @@
 * @author lzp
 * @date 2025-04-25
 */
@Data
public class ProductSimpleView {
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    private String productTitle;
    private String mainImage;
    private BigDecimal price;
    private BigDecimal originalPrice;  // 原价(用于显示划线价)
    //private Double rating;           // 评分
    //private Integer monthlySales;    // 月销量
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "条形码")
    private String barcode;
    @ApiModelProperty(value = "商品名称")
    private String name;
    @ApiModelProperty(value = "商品标题")
    private String title;
    @ApiModelProperty(value = "一级分类ID")
    private Long categoryId;
    @ApiModelProperty(value = "二级分类ID")
    private Long secondCategoryId;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "主图片")
    private String mainImageId;
    @ApiModelProperty(value = "主图地址")
    private String mainImageUrl;
    @ApiModelProperty(value = "商品描述")
    private String description;
    @ApiModelProperty(value = "销售价格")
    private BigDecimal price;
    @ApiModelProperty(value = "库存数量")
    private Integer stockQuantity;
    @ApiModelProperty(value = "起售数量")
    private Integer minPurchaseQuantity;
    @ApiModelProperty(value = "预警库存")
    private Integer warnStock;
    @ApiModelProperty(value = "重量(单位:g)")
    private Integer weight;
    @ApiModelProperty(value = "宽度(单位:厘米)")
    private Integer width;
    @ApiModelProperty(value = "长度(单位:厘米)")
    private Integer length;
    @ApiModelProperty(value = "高度(单位:厘米)")
    private Integer height;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "是否删除")
    private Integer deletedFlag;
    @ApiModelProperty(value = "版本号")
    private Long version;
}
oying-system/src/main/java/com/oying/modules/pc/search/domain/dto/NearbyStoreQueryCriteria.java
@@ -16,7 +16,7 @@
    private Double latitude;  // 中心点纬度
    @Min(100)
    @Max(5000)
    @Max(10000)
    private Integer radius = 1000; // 搜索半径(米)
    private Boolean onlyOpenNow = true; // 是否只查当前营业的
oying-system/src/main/java/com/oying/modules/pc/search/rest/StoreSearchController.java
@@ -36,6 +36,14 @@
        return R.success(toStoreSearchVo(storeSearchService.findNearStores(criteria)));
    }
    /**
     * 查询最近的店铺
     */
    @GetMapping("/find")
    public R<PageResult<StoreSearchView>> find(NearbyStoreQueryCriteria criteria) {
        return R.success(toStoreSearchVo(storeSearchService.findNearStores(criteria)));
    }
    private PageResult<StoreSearchView> toStoreSearchVo(PageResult<StoreSearchDto> resources) {
        PageResult<StoreSearchView> t = new PageResult<>();
        t.setTotalElements(resources.getTotalElements());
oying-system/src/main/java/com/oying/modules/pc/store/converter/StoreAssembler.java
New file
@@ -0,0 +1,32 @@
package com.oying.modules.pc.store.converter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
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.utils.ImageUtils;
import com.oying.utils.SecurityUtils;
public class StoreAssembler {
    public static Store to(StoreCreateRequest request) {
        Store store = new Store();
        BeanUtil.copyProperties(request, store, CopyOptions.create().setIgnoreNullValue(true));
        store.setMerchantId(SecurityUtils.getCurrentUserId());
        Long logoUploadFileId = request.getLogoUploadFileId();
        store.setLogoImageId(logoUploadFileId);
        store.setLogoImageUrl(ImageUtils.getPublicObjectUrl(logoUploadFileId));
        Long coverUploadFileId = request.getCoverUploadFileId();
        store.setCoverImageId(coverUploadFileId);
        store.setCoverImageUrl(ImageUtils.getPublicObjectUrl(coverUploadFileId));
        return store;
    }
    public static Store to(StoreUpdateRequest request) {
        Store store = new Store();
        BeanUtil.copyProperties(request, store, CopyOptions.create().setIgnoreNullValue(true));
        return store;
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/converter/StoreQualificationAssembler.java
New file
@@ -0,0 +1,32 @@
package com.oying.modules.pc.store.converter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.dto.StoreQualificationCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQualificationUpdateRequest;
import com.oying.modules.pc.utils.ImageUtils;
public class StoreQualificationAssembler {
    public static StoreQualification to(StoreQualificationCreateRequest request) {
        StoreQualification storeQualification = new StoreQualification();
        BeanUtil.copyProperties(request, storeQualification, CopyOptions.create().setIgnoreNullValue(true));
        storeQualification.setQualificationType(request.getType());
        Long imageUploadFileId = request.getImageUploadFileId();
        storeQualification.setQualificationImageId(imageUploadFileId);
        storeQualification.setQualificationImageUrl(ImageUtils.getPublicObjectUrl(imageUploadFileId));
        return storeQualification;
    }
    public static StoreQualification to(StoreQualificationUpdateRequest request) {
        StoreQualification storeQualification = new StoreQualification();
        BeanUtil.copyProperties(request, storeQualification, CopyOptions.create().setIgnoreNullValue(true));
        storeQualification.setQualificationType(request.getType());
        Long imageUploadFileId = request.getImageUploadFileId();
        storeQualification.setQualificationImageId(imageUploadFileId);
        storeQualification.setQualificationImageUrl(ImageUtils.getPublicObjectUrl(imageUploadFileId));
        return storeQualification;
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/Store.java
@@ -3,11 +3,12 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.oying.base.BaseEntity;
import com.oying.modules.pc.product.domain.Product;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.geo.Point;
@@ -17,6 +18,7 @@
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.List;
/**
 * @author lzp
@@ -28,7 +30,7 @@
public class Store extends BaseEntity implements Serializable {
    @TableId(value = "store_id", type = IdType.AUTO)
    @ApiModelProperty(value = "唯一标识")
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @NotNull
@@ -54,20 +56,20 @@
    private String businessScope;
    @NotNull
    @ApiModelProperty(value = "状态:1000-草稿  1002-正常  1003-暂停营业 1004-关闭")
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "店铺logo图片")
    private Long logoImageId;
    @ApiModelProperty(value = "店铺logo图片Url")
    private Long logoImageUrl;
    private String logoImageUrl;
    @ApiModelProperty(value = "店铺封面图")
    private Long coverImageId;
    @ApiModelProperty(value = "店铺封面图Url")
    private Long coverImageUrl;
    private String coverImageUrl;
    @ApiModelProperty(value = "店铺描述")
    private String description;
@@ -120,6 +122,36 @@
    @ApiModelProperty(value = "营业半径(米)")
    private Integer radius;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @NotNull
    @ApiModelProperty(value = "版本号")
    private Long version;
    @TableField(exist = false)
    @ApiModelProperty(value = "评分")
    private Double score = 0d;
    @TableField(exist = false)
    @ApiModelProperty(value = "配送距离")
    private Integer deliveryDuration = 0;
    @TableField(exist = false)
    @ApiModelProperty(value = "月销售量")
    private Integer monthlySales = 0;
    @TableField(exist = false)
    @ApiModelProperty(value = "资质")
    private List<StoreQualification> qualifications;
    @TableField(exist = false)
    @ApiModelProperty(value = "商品")
    private List<Product> products;
    public void copy(Store source) {
        BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));
    }
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreQualification.java
@@ -42,7 +42,7 @@
    private String qualificationName;
    @ApiModelProperty(value = "资质图片")
    private String qualificationImageId;
    private Long qualificationImageId;
    @ApiModelProperty(value = "资质图片")
    private String qualificationImageUrl;
@@ -53,7 +53,7 @@
    @ApiModelProperty(value = "有效期结束日期")
    private LocalTime endDate;
    @ApiModelProperty(value = "状态:1000-草稿 1001-有效 1002-无效")
    @ApiModelProperty(value = "状态")
    private Integer status;
    public void copy(StoreQualification source){
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreStatus.java
File was deleted
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreUser.java
New file
@@ -0,0 +1,39 @@
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 io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @author lzp
* @date 2025-06-05
**/
@Getter
@Setter
@TableName("sys_user_store")
public class StoreUser extends BaseEntity implements Serializable {
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "用户ID")
    private Long userId;
    @ApiModelProperty(value = "角色类型")
    private String roleType;
    @ApiModelProperty(value = "权限集(备用)")
    private String permissions;
    public void copy(StoreUser source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCategoryMerchantCreateRequest.java
@@ -8,6 +8,8 @@
@Data
public class StoreCategoryMerchantCreateRequest {
    private Long storeId;
    private Long parentId;
    @NotBlank
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCategoryMerchantUpdateRequest.java
@@ -1,10 +1,14 @@
package com.oying.modules.pc.store.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class StoreCategoryMerchantUpdateRequest {
    @ApiModelProperty(value = "ID")
    private Long categoryId;
    private Long parentId;
    private String name;
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCategoryQueryCriteria.java
@@ -3,6 +3,9 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
 * @author lzp
 * @date 2025-04-27
@@ -10,17 +13,28 @@
@Data
public class StoreCategoryQueryCriteria {
    @ApiModelProperty(value = "模糊查询")
    private String blurry;
    @ApiModelProperty(value = "ID")
    private Long categoryId;
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "类目层级:1-级类目 2-级类目")
    private Integer level;
    private Boolean recursive;
    @ApiModelProperty(value = "是否启用(0-否 1-是)")
    private Integer active;
    @ApiModelProperty(value = "递归处理")
    private Boolean recursive;
    @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/StoreCategoryUpdateRequest.java
@@ -5,11 +5,11 @@
@Data
public class StoreCategoryUpdateRequest {
    private Long categoryId;
    private Long storeId;
    private Long parentId;
    private Long categoryId;
    private String name;
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCreateRequest.java
@@ -6,6 +6,8 @@
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
@@ -20,71 +22,56 @@
    @ApiModelProperty(value = "店铺简称", example = "永辉超市(新纪元店)")
    private String storeShortName;*/
    @NotBlank
    @NotNull
    @ApiModelProperty(value = "店铺LOGO文件ID", example = "14567785444763247876234")
    private Long logoUploadFileId;
    /*@NotBlank
    @ApiModelProperty(value = "店铺门户图片ID", example = "276409837458893793939")
    private Long coverUploadFileId;*/
    private Long coverUploadFileId;
    @NotBlank
    @ApiModelProperty(value = "平台类目ID", example = "29784639387324848347230")
    private Long platformCategoryId;
    //private String businessCategory;
    @NotBlank
    @ApiModelProperty(value = "简介", example = "")
    @ApiModelProperty(value = "简介", example = " ")
    private String description;
    @NotBlank
    @ApiModelProperty(value = "店铺联系电话", example = "13800000001")
    private String contactPhone;
    @ApiModelProperty(value = "营业开始时间", example = "08:00")
    private LocalTime openTime;
    @ApiModelProperty(value = "营业结束时间", example = "22:00")
    private LocalTime closeTime;
    @NotBlank
    @ApiModelProperty(value = "店铺地址", example = "276409837458893793939")
    private String address;
    @NotBlank
    @NotNull
    @ApiModelProperty(value = "店铺坐标经度", example = "121.505978")
    private Double longitude;
    @NotBlank
    @NotNull
    @ApiModelProperty(value = "店铺坐标纬度", example = "31.144515")
    private Double latitude;
    @ApiModelProperty(value = "店铺资质", example = "")
    private List<StoreQualificationCreateRequest> qualificationList;
    @ApiModelProperty(value = "营业半径(米)")
    private Integer radius;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "店铺资质", example = "[]")
    private List<StoreQualificationCreateRequest> qualificationList = new ArrayList<>();
    public boolean hasQualificationList() {
        return CollUtil.isNotEmpty(this.qualificationList);
    }
    public static void main(String[] args) {
        StoreCreateRequest request = new StoreCreateRequest();
        request.setStoreName("上海三林小学");
        request.setLogoUploadFileId(1L);
        request.setDescription("");
        // request.setCoverUploadFileId(1L);
        request.setPlatformCategoryId(1L);
        request.setContactPhone("13599873421");
        request.setAddress("上海");
        request.setLongitude(121.505978D);
        request.setLatitude(31.144515D);
        List<StoreQualificationCreateRequest> qualificationList = new ArrayList<>();
        StoreQualificationCreateRequest sq1 = new StoreQualificationCreateRequest();
        sq1.setType(10001);
        // sq1.setName("营业执照");
        sq1.setImageUploadFileId("");
        qualificationList.add(sq1);
        StoreQualificationCreateRequest sq2 = new StoreQualificationCreateRequest();
        sq2.setType(10002);
        // sq2.setName("许可证");
        sq2.setImageUploadFileId("");
        qualificationList.add(sq2);
        request.setQualificationList(qualificationList);
        System.out.println(JSONUtil.toJsonStr(request));
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCustomerDetailDto.java
@@ -1,26 +1,80 @@
package com.oying.modules.pc.store.domain.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalTime;
@Data
public class StoreCustomerDetailDto {
    private String name;
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    private String logoUrl;
    @ApiModelProperty(value = "商户ID")
    private Long merchantId;
    @ApiModelProperty(value = "平台类目")
    private Long platformCategoryId;
    @ApiModelProperty(value = "店铺类型:1-自营 2-加盟 3-第三方")
    private Integer storeType;
    @ApiModelProperty(value = "店铺名称")
    private String storeName;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "店铺logo图片Url")
    private String logoImageUrl;
    @ApiModelProperty(value = "店铺封面图Url")
    private String coverImageUrl;
    @ApiModelProperty(value = "店铺描述")
    private String description;
    private String address;
    @ApiModelProperty(value = "联系电话")
    private String contactPhone;
    @ApiModelProperty(value = "营业开始时间")
    private LocalTime openTime;
    @ApiModelProperty(value = "营业结束时间")
    private LocalTime closeTime;
    private String businessHours;
    private String contactPhone;
    @ApiModelProperty(value = "详细地址")
    private String address;
    private Integer score;
    @ApiModelProperty(value = "经度")
    private Double longitude;
    private Integer deliveryDuration;
    @ApiModelProperty(value = "纬度")
    private Double latitude;
    private Integer monthlySales;
    @ApiModelProperty(value = "营业半径(米)")
    private Integer radius;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @TableField(exist = false)
    @ApiModelProperty(value = "评分")
    private Double score = 0d;
    @TableField(exist = false)
    @ApiModelProperty(value = "配送距离")
    private Integer deliveryDuration = 0;
    @TableField(exist = false)
    @ApiModelProperty(value = "月销售量")
    private Integer monthlySales = 0;
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreCustomerQueryCriteria.java
@@ -1,16 +1,49 @@
package com.oying.modules.pc.store.domain.dto;
import com.oying.utils.StringUtils;
import lombok.Data;
import org.springframework.util.DigestUtils;
import javax.validation.constraints.NotNull;
import java.util.StringJoiner;
@Data
public class StoreCustomerQueryCriteria {
    private Long StoreId;
    private Long storeId;
    private Long platformCategoryId;
    private String blurry;
    private Double longitude; // 中心点经度
    private Double latitude;  // 中心点纬度
    private Integer radius = 10000; // 搜索半径(米)
    private Integer limit = 20; // 返回数量限制
    private Integer page = 1;
    private Integer size = 10;
    public String buildConditionCacheKey() {
        StringJoiner baseKeyJoiner = new StringJoiner("|");
        if (platformCategoryId != null) {
            baseKeyJoiner.add("platformCategoryId=" + platformCategoryId);
        }
        if (StringUtils.isNotEmpty(blurry)) {
            baseKeyJoiner.add("blurry=" + blurry);
        }
        if (longitude != null && latitude != null) {
            baseKeyJoiner.add("longitude=" + longitude);
            baseKeyJoiner.add("latitude=" + latitude);
        }
        if (StringUtils.isNotEmpty(blurry)) {
            baseKeyJoiner.add("radius=" + radius);
        }
        // 使用MD5或SHA缩短键长度
        return "store:search:page:" + DigestUtils.md5DigestAsHex(baseKeyJoiner.toString().getBytes());
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreFieldUpdateRequest.java
@@ -1,5 +1,6 @@
package com.oying.modules.pc.store.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@@ -50,12 +51,12 @@
    public interface UpdateStoreDeliveryFeeGroup{}
    @NotBlank(groups = UpdateStoreDeliveryFeeGroup.class)
    @NotNull(groups = UpdateStoreDeliveryFeeGroup.class)
    private BigDecimal deliveryFee;
    public interface UpdateStoreDeliveryMinimumGroup{}
    @NotBlank(groups = UpdateStoreDeliveryMinimumGroup.class)
    @NotNull(groups = UpdateStoreDeliveryMinimumGroup.class)
    private BigDecimal deliveryMinimum;
    public interface UpdateStoreContactPhoneGroup{}
@@ -65,10 +66,10 @@
    public interface UpdateStoreBusinessHoursGroup{}
    @NotBlank(groups = UpdateStoreBusinessHoursGroup.class)
    @NotNull(groups = UpdateStoreBusinessHoursGroup.class)
    private LocalTime openTime;
    @NotBlank(groups = UpdateStoreBusinessHoursGroup.class)
    @NotNull(groups = UpdateStoreBusinessHoursGroup.class)
    private LocalTime closeTime;
    public interface UpdateStoreAddressGroup{}
@@ -78,15 +79,24 @@
    public interface UpdateStoreLocationGroup{}
    @NotBlank(groups = UpdateStoreLocationGroup.class)
    @NotNull(groups = UpdateStoreLocationGroup.class)
    private Double longitude;
    @NotBlank(groups = UpdateStoreLocationGroup.class)
    @NotNull(groups = UpdateStoreLocationGroup.class)
    private Double latitude;
    public interface UpdateStoreRadiusGroup{}
    @NotBlank(groups = UpdateStoreRadiusGroup.class)
    @NotNull(groups = UpdateStoreRadiusGroup.class)
    private Integer radius;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "版本号")
    private Long version;
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQualificationCreateRequest.java
@@ -11,7 +11,9 @@
@Data
public class StoreQualificationCreateRequest {
    @NotNull
    @ApiModelProperty(value = "关联的店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "资质类型", example = "1001")
    private Integer type;
@@ -19,8 +21,7 @@
    @ApiModelProperty(value = "资质名称", example = "营业执照")
    private String name;*/
    @NotBlank
    @ApiModelProperty(value = "资质图片ID", example = "30975645483838730008921")
    private String imageUploadFileId;
    private Long imageUploadFileId;
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQualificationQueryCriteria.java
@@ -11,7 +11,7 @@
public class StoreQualificationQueryCriteria{
    @ApiModelProperty(value = "店铺资质ID", example = "1")
    private Long id;
    private Long qualificationId;
    @ApiModelProperty(value = "店铺ID", example = "1")
    private Long storeId;
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQualificationUpdateRequest.java
@@ -9,19 +9,13 @@
@Data
public class StoreQualificationUpdateRequest {
    @NotBlank
    @ApiModelProperty(value = "资质ID", example = "317640956839788210948")
    private Long id;
    private Long qualificationId;
    @NotBlank(groups = {UpdateGroup.class})
    @ApiModelProperty(value = "资质类型", example = "1001")
    private Integer type;
    @NotBlank(groups = {UpdateGroup.class})
    @ApiModelProperty(value = "资质图片ID", example = "30975645483838730008921")
    private String imageUploadFileId;
    public interface UpdateGroup {
    }
    private Long imageUploadFileId;
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQueryCriteria.java
@@ -1,9 +1,12 @@
package com.oying.modules.pc.store.domain.dto;
import com.oying.utils.StringUtils;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.util.DigestUtils;
import java.io.Serializable;
import java.util.StringJoiner;
/**
 * @author lzp
@@ -19,13 +22,43 @@
    private Integer status;
    private Long storeId;
    private Long platformCategoryId;
    private String blurry;
    private Double longitude; // 中心点经度
    private Double latitude;  // 中心点纬度
    private Integer radius = 10000; // 搜索半径(米)
    private Integer limit = 20; // 返回数量限制
    @ApiModelProperty(value = "页码", example = "1")
    private Integer page = 1;
    @ApiModelProperty(value = "每页数据量", example = "10")
    private Integer size = 10;
    @ApiModelProperty(value = "偏移量", hidden = true)
    private long offset;
    public String buildConditionCacheKey() {
        StringJoiner baseKeyJoiner = new StringJoiner("|");
        if (platformCategoryId != null) {
            baseKeyJoiner.add("platformCategoryId=" + platformCategoryId);
        }
        if (StringUtils.isNotEmpty(blurry)) {
            baseKeyJoiner.add("blurry=" + blurry);
        }
        if (longitude != null && latitude != null) {
            baseKeyJoiner.add("longitude=" + longitude);
            baseKeyJoiner.add("latitude=" + latitude);
        }
        if (StringUtils.isNotEmpty(blurry)) {
            baseKeyJoiner.add("radius=" + radius);
        }
        // 使用MD5或SHA缩短键长度
        return "store:search:page:" + DigestUtils.md5DigestAsHex(baseKeyJoiner.toString().getBytes());
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreUpdateRequest.java
New file
@@ -0,0 +1,103 @@
package com.oying.modules.pc.store.domain.dto;
import com.oying.modules.pc.product.domain.Product;
import com.oying.modules.pc.store.domain.StoreQualification;
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.math.BigDecimal;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
/**
 * @author lzp
 * @date 2025-04-21
 **/
@Getter
@Setter
public class StoreUpdateRequest implements Serializable {
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "商户ID")
    private Long merchantId;
    @ApiModelProperty(value = "平台类目")
    private Long platformCategoryId;
    @ApiModelProperty(value = "店铺类型")
    private Integer storeType;
    @ApiModelProperty(value = "店铺名称")
    private String storeName;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "店铺logo图片")
    private Long logoImageId;
    @ApiModelProperty(value = "店铺logo图片Url")
    private String logoImageUrl;
    @ApiModelProperty(value = "店铺封面图")
    private Long coverImageId;
    @ApiModelProperty(value = "店铺封面图Url")
    private String coverImageUrl;
    @ApiModelProperty(value = "店铺描述")
    private String description;
    @ApiModelProperty(value = "配送费用")
    private BigDecimal deliveryFee;
    @ApiModelProperty(value = "起送金额")
    private BigDecimal deliveryMinimum;
    @ApiModelProperty(value = "联系电话")
    private String contactPhone;
    @ApiModelProperty(value = "营业开始时间")
    private LocalTime openTime;
    @ApiModelProperty(value = "营业结束时间")
    private LocalTime closeTime;
    @ApiModelProperty(value = "详细地址")
    private String address;
    @ApiModelProperty(value = "经度")
    private Double longitude;
    @ApiModelProperty(value = "纬度")
    private Double latitude;
    @ApiModelProperty(value = "营业半径(米)")
    private Integer radius;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "版本号")
    private Long version;
    @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/StoreCategoryEnum.java
New file
@@ -0,0 +1,28 @@
package com.oying.modules.pc.store.domain.enums;
import lombok.Getter;
@Getter
public enum StoreCategoryEnum {
    NOT(0, "已禁用"),
    YES(1, "已启用");
    private final Integer value;
    private final String reasonPhrase;
    StoreCategoryEnum(int value, String reasonPhrase) {
        this.value = value;
        this.reasonPhrase = reasonPhrase;
    }
    public static StoreCategoryEnum get(Integer code) {
        for (StoreCategoryEnum value : values()) {
            if (value.value.equals(code)) {
                return value;
            }
        }
        return null;
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/enums/StoreStatusEnum.java
@@ -1,21 +1,33 @@
package com.oying.modules.pc.store.domain.enums;
import lombok.Getter;
@Getter
public enum StoreStatusEnum {
    creating(1000, "筹备中"),
    reviewing(2000, "审核中"),
    business_open(3000, ""),
    business_paused(3001, ""),
    business_banned(3002, ""),
    business_suspended(3003, ""),
    closed(1001, ""),
    deleted(1002, "");
    DRAFT(1000, "创建中"),
    PENDING(1001, "待审核"),
    UNDER_REVIEW(1002, "审核中"),
    REJECTED(1003, "拒绝"),
    APPROVED(1004, "同意"),
    COMING_SOON(2100, "即将开业"),
    OPEN(2101, "营业中"),
    CLOSED(2102, "停止营业");
    private final int value;
    private final Integer value;
    private final String reasonPhrase;
    private StoreStatusEnum(int value, String reasonPhrase) {
    StoreStatusEnum(Integer value, String reasonPhrase) {
        this.value = value;
        this.reasonPhrase = reasonPhrase;
    }
    public static StoreStatusEnum get(Integer code) {
        for (StoreStatusEnum value : values()) {
            if (value.value.equals(code)) {
                return value;
            }
        }
        return null;
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreMapper.java
@@ -5,6 +5,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.search.domain.dto.NearbyStoreQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreCustomerQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.modules.pc.search.domain.dto.StoreSearchDto;
import org.apache.ibatis.annotations.Mapper;
@@ -31,4 +32,8 @@
    IPage<StoreSearchDto> queryNearStoreWithProduct(@Param("criteria") NearbyStoreQueryCriteria criteria, Page<StoreSearchDto> page);
    List<Long> queryStoreIds(@Param("criteria") StoreQueryCriteria criteria);
    List<Store> queryUserStores(@Param("userId") Long userId);
}
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreUserMapper.java
New file
@@ -0,0 +1,18 @@
package com.oying.modules.pc.store.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.oying.modules.pc.store.domain.StoreUser;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
/**
* @author lzp
* @date 2025-06-05
**/
@Mapper
public interface StoreUserMapper extends BaseMapper<StoreUser> {
    @Insert("INSERT INTO sys_user_store(store_id, user_id, role_type, create_by, permissions, create_time, update_by, update_time) VALUES(#{storeId}, #{userId}, #{roleType}, #{permissions}, #{createBy}, #{createTime}, #{updateBy}, #{updateTime})")
    int insert(StoreUser storeUser);
}
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCategoryController.java
@@ -1,25 +1,29 @@
package com.oying.modules.pc.store.rest;
import com.oying.annotation.Log;
import com.oying.utils.R;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.modules.pc.store.domain.StoreCategory;
import com.oying.modules.pc.store.service.StoreCategoryService;
import com.oying.modules.pc.store.domain.dto.StoreCategoryCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreCategoryQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreCategoryUpdateRequest;
import com.oying.modules.pc.store.service.StoreCategoryService;
import com.oying.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import java.util.List;
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 io.swagger.annotations.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
/**
* @author lzp
* @date 2025-04-24
**/
 * @author lzp
 * @date 2025-04-24
 **/
@Api(tags = "商品中心:店铺类目")
@RestController
@RequiredArgsConstructor
@@ -28,35 +32,48 @@
    private final StoreCategoryService storeCategoryService;
    @GetMapping
    @ApiOperation("查询api/store/category")
    public ResponseEntity<?> queryStoreCategory(StoreCategoryQueryCriteria criteria){
    @GetMapping(value = "/list")
    @ApiOperation("查询")
    public ResponseEntity<?> getStoreCategories(StoreCategoryQueryCriteria criteria) {
        return ResponseEntity.ok(R.success(storeCategoryService.queryAll(criteria)));
    }
    @GetMapping(value = "/page")
    @ApiOperation("查询")
    public ResponseEntity<?> getStoreCategoriesByPage(StoreCategoryQueryCriteria criteria) {
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return ResponseEntity.ok(R.success(storeCategoryService.queryAll(criteria, page)));
    }
    @GetMapping(value = "/{categoryId}")
    @ApiOperation("查询")
    public ResponseEntity<?> getStoreCategory(@PathVariable Long categoryId) {
        return ResponseEntity.ok(R.success(storeCategoryService.getById(categoryId)));
    }
    @PostMapping
    @Log("新增api/store")
    @ApiOperation("新增api/store")
    @PreAuthorize("@el.check('storeCategory:add')")
    public ResponseEntity<?> createStoreCategory(@Validated @RequestBody StoreCategory resources){
        storeCategoryService.create(resources);
    @ApiOperation("新增")
    // @PreAuthorize("@el.check('storeCategory:add')")
    public ResponseEntity<?> createStoreCategory(@Validated @RequestBody StoreCategoryCreateRequest request) {
        StoreCategory storeCategory = new StoreCategory();
        BeanUtil.copyProperties(request, storeCategory, CopyOptions.create().setIgnoreNullValue(true));
        storeCategoryService.create(storeCategory);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    @PutMapping
    @Log("修改api/store")
    @ApiOperation("修改api/store")
    @PreAuthorize("@el.check('storeCategory:edit')")
    public ResponseEntity<?> updateStoreCategory(@Validated @RequestBody StoreCategory resources){
        storeCategoryService.update(resources);
    @ApiOperation("修改")
    // @PreAuthorize("@el.check('storeCategory:edit')")
    public ResponseEntity<?> updateStoreCategory(@Validated @RequestBody StoreCategoryUpdateRequest request) {
        StoreCategory storeCategory = new StoreCategory();
        BeanUtil.copyProperties(request, storeCategory, CopyOptions.create().setIgnoreNullValue(true));
        storeCategoryService.update(storeCategory);
        return ResponseEntity.noContent().build();
    }
    @DeleteMapping
    @Log("删除api/store")
    @ApiOperation("删除api/store")
    @PreAuthorize("@el.check('storeCategory:del')")
    @ApiOperation("删除")
    // @PreAuthorize("@el.check('storeCategory:del')")
    public ResponseEntity<?> deleteStoreCategory(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
        storeCategoryService.deleteAll(ids);
        return ResponseEntity.noContent().build();
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCategoryCustomerController.java
@@ -5,6 +5,7 @@
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.BooleanUtil;
import com.oying.modules.pc.store.domain.enums.StoreCategoryEnum;
import com.oying.utils.R;
import com.oying.modules.pc.store.domain.StoreCategory;
import com.oying.modules.pc.store.domain.dto.StoreCategoryQueryCriteria;
@@ -39,7 +40,7 @@
        StoreCategoryQueryCriteria criteria = new StoreCategoryQueryCriteria();
        criteria.setStoreId(storeId);
        criteria.setActive(1);
        criteria.setActive(StoreCategoryEnum.YES.getValue());
        List<StoreCategory> categoryList = storeCategoryService.queryAll(criteria);
        List<CustomerStoreCategoryView> categoryViewList = Optional.ofNullable(categoryList).orElse(ListUtil.empty()).stream().map(i -> {
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCategoryMerchantController.java
@@ -42,8 +42,8 @@
    @ApiOperation("查询店铺类目")
    //@PreAuthorize("@el.check('merchant:storeCategory:list')" +
    //        " and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> getList(@PathVariable Long storeId,
                                     @RequestParam(value = "recursive", required = false) Boolean recursive) {
    public ResponseEntity<?> getStoreCategories(@PathVariable Long storeId,
                                                @RequestParam(value = "recursive", required = false) Boolean recursive) {
        StoreCategoryQueryCriteria criteria = new StoreCategoryQueryCriteria();
        criteria.setStoreId(storeId);
@@ -77,7 +77,7 @@
    @ApiOperation("查询店铺类目")
    //@PreAuthorize("@el.check('merchant:storeCategory:list')" +
    //        " and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> getById(@PathVariable Long categoryId,
    public ResponseEntity<?> getStoreCategoryById(@PathVariable Long categoryId,
                                     @RequestParam(value = "recursive", required = false) Boolean recursive) {
        StoreCategory storeCategory = storeCategoryService.getById(categoryId);
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreController.java
@@ -1,15 +1,21 @@
package com.oying.modules.pc.store.rest;
import com.oying.utils.R;
import com.oying.modules.pc.common.id.StoreIdGenerator;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.*;
import com.oying.modules.pc.store.domain.dto.StoreCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreUpdateRequest;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.utils.SecurityUtils;
import com.oying.utils.R;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Optional;
/**
 * 店铺
@@ -24,43 +30,43 @@
    private final StoreService storeService;
    @GetMapping(value = "/page")
    public R<?> getStoresByPage(StoreQueryCriteria criteria) {
        return R.success(storeService.queryByPage(criteria));
    @GetMapping(value = "/list")
    @ApiOperation("查询所有店铺")
    public ResponseEntity<?> getStores(StoreQueryCriteria criteria) {
        List<Store> storeList = Optional.ofNullable(storeService.queryAll(criteria)).orElse(ListUtil.empty());
        return ResponseEntity.ok(R.success(storeList));
    }
    @GetMapping(value = "/list")
    public R<?> getStores(StoreQueryCriteria criteria) {
        return R.success(storeService.queryAll(criteria));
    @GetMapping(value = "/page")
    @ApiOperation("查询所有店铺")
    public ResponseEntity<?> getStoresByPage(StoreQueryCriteria criteria) {
        return ResponseEntity.ok(R.success(storeService.queryByPage(criteria)));
    }
    @GetMapping(value = "/{storeId}")
    public R<?> getStoreById(@PathVariable("storeId") Long storeId) {
        return R.success(storeService.getById(storeId));
    @ApiOperation("查询店铺")
    public ResponseEntity<?> getStoreById(@PathVariable Long storeId) {
        return ResponseEntity.ok(R.success(storeService.getById(storeId)));
    }
    @GetMapping(value = "/{storeId}/details")
    public R<?> getStoreDetailsById(@PathVariable("storeId") Long storeId) {
        return R.success(storeService.getById(storeId));
    public ResponseEntity<?> getStoreDetailsById(@PathVariable("storeId") Long storeId) {
        return ResponseEntity.ok(R.success(storeService.getById(storeId)));
    }
    @PostMapping(value = "/createEmpty")
    public R<?> createEmpty(@RequestBody StoreCreateRequest request) {
        Store store = new Store();
        store.setStoreId(StoreIdGenerator.getId());
        store.setMerchantId(SecurityUtils.getCurrentUserId());
        storeService.save(store);
        return R.success();
    @PostMapping
    @ApiOperation("创建店铺")
    public ResponseEntity<?> create(@RequestBody StoreCreateRequest request) {
        storeService.create(request);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    /**
     * 修改店铺信息
     */
    @PostMapping(value = "/{storeId}")
    public R<?> update(@PathVariable("storeId") Long storeId, @RequestBody Store store) {
        store.setStoreId(storeId);
        storeService.updateById(store);
        return R.success();
    @ApiOperation("修改店铺")
    public ResponseEntity<?> update(@PathVariable("storeId") Long storeId, @RequestBody StoreUpdateRequest request) {
        request.setStoreId(ObjectUtil.defaultIfNull(request.getStoreId(), storeId));
        storeService.update(request);
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCustomerController.java
@@ -1,14 +1,30 @@
package com.oying.modules.pc.store.rest;
import com.oying.utils.R;
import com.oying.modules.pc.store.domain.dto.StoreCustomerDetailDto;
import com.oying.modules.pc.store.domain.dto.StoreCustomerQueryCriteria;
import cn.hutool.core.util.ObjUtil;
import com.oying.modules.pc.product.domain.Product;
import com.oying.modules.pc.product.domain.dto.ProductQueryCriteria;
import com.oying.modules.pc.product.domain.enums.ProductStatusEnum;
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.service.StoreQualificationService;
import com.oying.modules.pc.store.service.StoreQueryService;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.modules.pc.store.view.CustomerStoreView;
import com.oying.modules.pc.utils.BusinessHoursUtils;
import com.oying.utils.PageResult;
import com.oying.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * 店铺
@@ -22,15 +38,48 @@
@RequiredArgsConstructor
public class StoreCustomerController {
    private final StoreService storeService;
    private final StoreQueryService storeQueryService;
    private final StoreQualificationService storeQualificationService;
    private final ProductService productService;
    @GetMapping(value = "/page")
    @ApiOperation("查询店铺")
    public ResponseEntity<?> getStoresByPage(StoreQueryCriteria criteria) {
        PageResult<Store> pagedStores = storeQueryService.findPagedStores(criteria);
        List<Store> stores = pagedStores.getContent();
        for (Store store : stores) {
            store.setProducts(this.getProductsByStoreId(store.getStoreId()));
        }
        return ResponseEntity.ok(R.success(pagedStores));
    }
    @GetMapping(value = "/{storeId}")
    @ApiOperation("查询店铺")
    public ResponseEntity<?> getCustomerStoreById(@PathVariable("storeId") Long storeId) {
        StoreCustomerQueryCriteria criteria = new StoreCustomerQueryCriteria();
    public ResponseEntity<?> getStoreById(@PathVariable("storeId") Long storeId) {
        Store store = storeService.getById(storeId);
        CustomerStoreView view = new CustomerStoreView();
        BeanUtils.copyProperties(store, view);
        view.setBusinessHours(BusinessHoursUtils.formatBusinessHours(store.getOpenTime(), store.getCloseTime()));
        return ResponseEntity.ok(R.success(view));
    }
    @GetMapping(value = "/{storeId}/details")
    @ApiOperation("查询店铺")
    public ResponseEntity<?> getStoreDetailsById(@PathVariable("storeId") Long storeId) {
        Store store = storeService.getById(storeId);
        if (ObjUtil.isNotEmpty(store)) {
            store.setQualifications(storeQualificationService.queryByStoreId(storeId));
        }
        return ResponseEntity.ok(R.success(store));
    }
    private List<Product> getProductsByStoreId(Long storeId) {
        ProductQueryCriteria criteria = new ProductQueryCriteria();
        criteria.setStoreId(storeId);
        StoreCustomerDetailDto detailDto = storeQueryService.getCustomerStoreDetail(criteria);
        return ResponseEntity.ok(R.success(detailDto));
        criteria.setStatus(ProductStatusEnum.AVAILABLE.getValue());
        criteria.setLimit(3);
        return productService.queryAll(criteria);
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreMerchantController.java
@@ -1,6 +1,12 @@
package com.oying.modules.pc.store.rest;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
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.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreCreateRequest;
@@ -39,21 +45,24 @@
    private final StoreService storeService;
    private final StoreCreateService storeCreateService;
    private final StoreQualificationService storeQualificationService;
    @GetMapping(value = "/list")
    @ApiOperation("查询所有店铺")
    //@PreAuthorize("@el.check('merchant:store:list')")
    public ResponseEntity<?> getList() {
        StoreQueryCriteria criteria = new StoreQueryCriteria();
        criteria.setMerchantId(SecurityUtils.getCurrentUserId());
        //criteria.setStatus();
        List<Store> storeList = Optional.ofNullable(storeService.queryAll(criteria)).orElse(ListUtil.empty());
        List<StoreSimpleView> storeViewList = storeList.stream().map(s -> {
        List<Store> stores = Optional.ofNullable(storeService.queryUserStores(SecurityUtils.getCurrentUserId())).orElse(ListUtil.empty());
        return ResponseEntity.ok(R.success(stores));
    }
    @GetMapping(value = "/simple-list")
    @ApiOperation("查询所有店铺")
    //@PreAuthorize("@el.check('merchant:store:list')")
    public ResponseEntity<?> getSimpleStores() {
        List<Store> stores = Optional.ofNullable(storeService.queryUserStores(SecurityUtils.getCurrentUserId())).orElse(ListUtil.empty());
        List<StoreSimpleView> storeViewList = stores.stream().map(i -> {
            StoreSimpleView view = new StoreSimpleView();
            view.setId(s.getStoreId());
            view.setName(s.getStoreName());
            view.setLogoUrl("");
            view.setStatus(s.getStatus());
            BeanUtil.copyProperties(i, view, CopyOptions.create().setIgnoreNullValue(true));
            return view;
        }).collect(Collectors.toList());
        return ResponseEntity.ok(R.success(storeViewList));
@@ -61,14 +70,23 @@
    @GetMapping(value = "/{storeId}")
    @ApiOperation("查询店铺")
    //@PreAuthorize("@el.check('merchant:store:getById')" +
    //             " and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> getById(@PathVariable Long storeId) {
    //@PreAuthorize("@el.check('merchant:store:getById')")
    public ResponseEntity<?> getStoreById(@PathVariable Long storeId) {
        Store store = storeService.getById(storeId);
        StoreMerchantView view = new StoreMerchantView();
        BeanUtils.copyProperties(store, view);
        view.setLogoUrl("");
        return ResponseEntity.ok(R.success(view));
    }
    @GetMapping(value = "/{storeId}/details")
    @ApiOperation("查询店铺")
    //@PreAuthorize("@el.check('merchant:store:getById')")
    public ResponseEntity<?> getStoreDetailsById(@PathVariable Long storeId) {
        Store store = storeService.getById(storeId);
        if (ObjUtil.isNotEmpty(store)) {
            store.setQualifications(storeQualificationService.queryByStoreId(storeId));
        }
        return ResponseEntity.ok(R.success(store));
    }
    @PostMapping
@@ -86,10 +104,9 @@
    @ApiOperation("修改店铺")
    //@PreAuthorize("@el.check('merchant:store:update')" +
    //        " and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> update(@PathVariable("storeId") Long storeId,
                       @RequestBody Store store) {
        store.setStoreId(storeId);
        storeService.updateById(store);
    public ResponseEntity<?> update(@PathVariable("storeId") Long storeId, @RequestBody StoreUpdateRequest request) {
        request.setStoreId(ObjectUtil.defaultIfNull(request.getStoreId(), storeId));
        storeService.update(request);
        return ResponseEntity.noContent().build();
    }
@@ -103,7 +120,7 @@
    public ResponseEntity<?> updateLogo(@PathVariable("storeId") Long storeId,
                           @Validated(value = StoreFieldUpdateRequest.UpdateStoreLogoImageGroup.class)
                           @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateLogo(storeId, request.getLogoImageUploadId());
        storeService.updateLogo(storeId, request.getLogoImageUploadId(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -117,7 +134,7 @@
    public ResponseEntity<?> updateName(@PathVariable("storeId") Long storeId,
                           @Validated(value = StoreFieldUpdateRequest.UpdateStoreNameGroup.class)
                           @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateName(storeId, request.getStoreName());
        storeService.updateName(storeId, request.getStoreName(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -131,7 +148,7 @@
    public ResponseEntity<?> updateDescription(@PathVariable("storeId") Long storeId,
                                  @Validated(value = StoreFieldUpdateRequest.UpdateStoreDescriptionGroup.class)
                                  @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateDescription(storeId, request.getDescription());
        storeService.updateDescription(storeId, request.getDescription(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -145,7 +162,7 @@
    public ResponseEntity<?> updateContactPhone(@PathVariable("storeId") Long storeId,
                                   @Validated(value = StoreFieldUpdateRequest.UpdateStoreContactPhoneGroup.class)
                                   @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateContactPhone(storeId, request.getContactPhone());
        storeService.updateContactPhone(storeId, request.getContactPhone(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -159,7 +176,7 @@
    public ResponseEntity<?> updateAddress(@PathVariable("storeId") Long storeId,
                              @Validated(value = StoreFieldUpdateRequest.UpdateStoreAddressGroup.class)
                              @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateAddress(storeId, request.getAddress());
        storeService.updateAddress(storeId, request.getAddress(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -173,7 +190,7 @@
    public ResponseEntity<?> updateLocation(@PathVariable("storeId") Long storeId,
                               @Validated(value = StoreFieldUpdateRequest.UpdateStoreLocationGroup.class)
                               @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateLocation(storeId, request.getLongitude(), request.getLatitude());
        storeService.updateLocation(storeId, request.getLongitude(), request.getLatitude(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -187,7 +204,7 @@
    public ResponseEntity<?> updateRadius(@PathVariable("storeId") Long storeId,
                             @Validated(value = StoreFieldUpdateRequest.UpdateStoreRadiusGroup.class)
                             @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateRadius(storeId, request.getRadius());
        storeService.updateRadius(storeId, request.getRadius(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -201,7 +218,7 @@
    public ResponseEntity<?> updatePlatformCategory(@PathVariable("storeId") Long storeId,
                                       @Validated(value = StoreFieldUpdateRequest.UpdateStorePlatformCategoryGroup.class)
                                       @RequestBody StoreFieldUpdateRequest request) {
        storeService.updatePlatformCategory(storeId, request.getPlatformCategoryId());
        storeService.updatePlatformCategory(storeId, request.getPlatformCategoryId(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -215,7 +232,7 @@
    public ResponseEntity<?> updateBusinessHours(@PathVariable("storeId") Long storeId,
                                    @Validated(value = StoreFieldUpdateRequest.UpdateStoreBusinessHoursGroup.class)
                                    @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateBusinessHours(storeId, request.getOpenTime(), request.getCloseTime());
        storeService.updateBusinessHours(storeId, request.getOpenTime(), request.getCloseTime(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -229,7 +246,7 @@
    public ResponseEntity<?> updateDeliveryMinimum(@PathVariable("storeId") Long storeId,
                                      @Validated(value = StoreFieldUpdateRequest.UpdateStoreDeliveryMinimumGroup.class)
                                      @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateDeliveryMinimum(storeId, request.getDeliveryMinimum());
        storeService.updateDeliveryMinimum(storeId, request.getDeliveryMinimum(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -243,7 +260,7 @@
    public ResponseEntity<?> updateDeliveryFee(@PathVariable("storeId") Long storeId,
                                  @Validated(value = StoreFieldUpdateRequest.UpdateStoreDeliveryFeeGroup.class)
                                  @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateDeliveryFee(storeId, request.getDeliveryFee());
        storeService.updateDeliveryFee(storeId, request.getDeliveryFee(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -257,7 +274,7 @@
    public ResponseEntity<?> businessStatus(@PathVariable("storeId") Long storeId,
                               @Validated(value = StoreFieldUpdateRequest.UpdateStoreBusinessStatusGroup.class)
                               @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateStatus(storeId, request.getBusinessStatus());
        storeService.updateStatus(storeId, request.getBusinessStatus(), request.getVersion());
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreQualificationController.java
@@ -2,8 +2,10 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.annotation.Log;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.converter.StoreQualificationAssembler;
import com.oying.modules.pc.store.domain.dto.StoreQualificationCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQualificationQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreQualificationUpdateRequest;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.utils.R;
import io.swagger.annotations.Api;
@@ -12,7 +14,6 @@
import lombok.RequiredArgsConstructor;
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.*;
@@ -25,43 +26,57 @@
@RestController
@RequiredArgsConstructor
@Api(tags = "店铺资质")
@RequestMapping("/api/storeQualification")
@RequestMapping("/api/pc/storeQualification")
public class StoreQualificationController {
    private final StoreQualificationService storeQualificationService;
    @GetMapping
    @GetMapping(value = "/list")
    @ApiOperation("查询店铺资质")
    @PreAuthorize("@el.check('storeQualification:list')")
    public ResponseEntity<Object> queryStoreQualification(StoreQualificationQueryCriteria criteria) {
    // @PreAuthorize("@el.check('storeQualification:list')")
    public ResponseEntity<?> getStoreQualifications(StoreQualificationQueryCriteria criteria) {
        return ResponseEntity.ok(R.success(storeQualificationService.queryAll(criteria)));
    }
    @GetMapping(value = "/page")
    @ApiOperation("查询店铺资质")
    // @PreAuthorize("@el.check('storeQualification:list')")
    public ResponseEntity<?> getStoreQualificationsByPage(StoreQualificationQueryCriteria criteria) {
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return new ResponseEntity<>(R.success(storeQualificationService.queryAll(criteria, page)), HttpStatus.OK);
        return ResponseEntity.ok(R.success(storeQualificationService.queryAll(criteria, page)));
    }
    @GetMapping(value = "/{qualificationId}")
    @ApiOperation("查询店铺资质")
    // @PreAuthorize("@el.check('storeQualification:list')")
    public ResponseEntity<?> getStoreQualificationById(@PathVariable Long qualificationId) {
        return ResponseEntity.ok(R.success(storeQualificationService.getById(qualificationId)));
    }
    @PostMapping
    @Log("新增店铺资质")
    @ApiOperation("新增店铺资质")
    @PreAuthorize("@el.check('storeQualification:add')")
    public ResponseEntity<Object> createStoreQualification(@Validated @RequestBody StoreQualification resources) {
        storeQualificationService.create(resources);
        return new ResponseEntity<>(R.success(),HttpStatus.CREATED);
    // @PreAuthorize("@el.check('storeQualification:add')")
    public ResponseEntity<Object> createStoreQualification(@Validated @RequestBody StoreQualificationCreateRequest request) {
        storeQualificationService.create(StoreQualificationAssembler.to(request));
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    @PutMapping
    @Log("修改店铺资质")
    @ApiOperation("修改店铺资质")
    @PreAuthorize("@el.check('storeQualification:edit')")
    public ResponseEntity<Object> updateStoreQualification(@Validated @RequestBody StoreQualification resources) {
        storeQualificationService.update(resources);
        return new ResponseEntity<>(R.success(),HttpStatus.NO_CONTENT);
    // @PreAuthorize("@el.check('storeQualification:edit')")
    public ResponseEntity<Object> updateStoreQualification(@Validated @RequestBody StoreQualificationUpdateRequest request) {
        storeQualificationService.update(request);
        return ResponseEntity.noContent().build();
    }
    @DeleteMapping
    @Log("删除店铺资质")
    @ApiOperation("删除店铺资质")
    @PreAuthorize("@el.check('storeQualification:del')")
    // @PreAuthorize("@el.check('storeQualification:del')")
    public ResponseEntity<Object> deleteStoreQualification(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
        storeQualificationService.deleteAll(ids);
        return new ResponseEntity<>(R.success(),HttpStatus.OK);
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreQualificationCustomerController.java
@@ -1,5 +1,7 @@
package com.oying.modules.pc.store.rest;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import com.oying.utils.R;
@@ -34,20 +36,18 @@
    @GetMapping("/list")
    @ApiOperation("查询店铺资质")
    //@PreAuthorize("@el.check('customer:storeQualification:list')")
    public ResponseEntity<?> getList(@PathVariable Long storeId) {
        if (ObjUtil.isEmpty(storeId)) {
            return ResponseEntity.ok(R.success(ListUtil.empty()));
        }
    public ResponseEntity<?> getQualificationsByStoreId(@PathVariable Long storeId) {
        StoreQualificationQueryCriteria criteria = new StoreQualificationQueryCriteria();
        criteria.setStoreId(storeId);
        List<StoreQualification> qualificationList = storeQualificationService.queryAll(criteria);
        List<CustomerStoreQualificationView> viewList = Optional.ofNullable(qualificationList).orElse(ListUtil.empty()).stream().map(i -> {
            CustomerStoreQualificationView view = new CustomerStoreQualificationView();
            BeanUtils.copyProperties(i, view);
            view.setType(i.getQualificationType());
            view.setName(i.getQualificationName());
            view.setImageUrl("");
            return view;
        List<CustomerStoreQualificationView> viewList = Optional.ofNullable(qualificationList).orElse(ListUtil.empty())
                .stream().map(i -> {
                    CustomerStoreQualificationView view = new CustomerStoreQualificationView();
                    BeanUtil.copyProperties(i, view, CopyOptions.create().setIgnoreNullValue(true));
                    view.setType(i.getQualificationType());
                    view.setName(i.getQualificationName());
                    view.setImageUrl(i.getQualificationImageUrl());
                    return view;
        }).collect(Collectors.toList());
        return ResponseEntity.ok(R.success(viewList));
    }
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreQualificationMerchantController.java
@@ -1,14 +1,17 @@
package com.oying.modules.pc.store.rest;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.ListUtil;
import com.oying.annotation.Log;
import com.oying.utils.R;
import com.oying.modules.pc.store.converter.StoreQualificationAssembler;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.dto.StoreQualificationCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQualificationQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreQualificationUpdateRequest;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.modules.pc.store.view.StoreQualificationMerchantView;
import com.oying.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -44,10 +47,11 @@
        List<StoreQualification> storeQualificationList = storeQualificationService.queryAll(criteria);
        List<StoreQualificationMerchantView> viewList = Optional.ofNullable(storeQualificationList).orElse(ListUtil.empty()).stream().map(i -> {
            StoreQualificationMerchantView view = new StoreQualificationMerchantView();
            BeanUtil.copyProperties(i, view, CopyOptions.create().setIgnoreNullValue(true));
            view.setId(i.getQualificationId());
            view.setType(i.getQualificationType());
            view.setName(i.getQualificationName());
            view.setImageUrl("");
            view.setImageUrl(i.getQualificationImageUrl());
            return view;
        }).collect(Collectors.toList());
@@ -60,13 +64,9 @@
    //@PreAuthorize("@el.check('merchant:storeQualification:add') " +
    //        "and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> create(@PathVariable Long storeId,
                                         @Validated @RequestBody StoreQualificationCreateRequest request) {
        StoreQualification resources = new StoreQualification();
        resources.setStoreId(storeId);
        resources.setQualificationType(request.getType());
        resources.setQualificationImageId(request.getImageUploadFileId());
        storeQualificationService.create(resources);
                                    @Validated @RequestBody StoreQualificationCreateRequest request) {
        request.setStoreId(Optional.ofNullable(request.getStoreId()).orElse(storeId));
        storeQualificationService.create(StoreQualificationAssembler.to(request));
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
@@ -76,15 +76,10 @@
    //@PreAuthorize("@el.check('merchant:storeQualification:edit') " +
    //        "and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> update(@PathVariable Long storeId,
                                         @PathVariable Long qualificationId,
                                         @Validated @RequestBody StoreQualificationUpdateRequest request) {
        StoreQualification resources = new StoreQualification();
        resources.setQualificationId(qualificationId);
        resources.setStoreId(storeId);
        resources.setQualificationType(request.getType());
        resources.setQualificationImageId(request.getImageUploadFileId());
        storeQualificationService.update(resources);
                                    @PathVariable Long qualificationId,
                                    @Validated @RequestBody StoreQualificationUpdateRequest request) {
        request.setQualificationId(Optional.ofNullable(request.getQualificationId()).orElse(qualificationId));
        storeQualificationService.update(request);
        return ResponseEntity.noContent().build();
    }
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreQualificationService.java
@@ -7,6 +7,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.dto.StoreQualificationQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreQualificationUpdateRequest;
import com.oying.utils.PageResult;
/**
@@ -32,6 +33,20 @@
    List<StoreQualification> queryAll(StoreQualificationQueryCriteria criteria);
    /**
     * 查询
     * @param ids 条件参数
     * @return List<StoreQualification>
     */
    List<StoreQualification> queryBatchIds(List<Long> ids);
    /**
     * 查询
     * @param storeId 条件参数
     * @return List<StoreQualification>
     */
    List<StoreQualification> queryByStoreId(Long storeId);
    /**
    * 创建
    * @param resources /
    */
@@ -45,9 +60,15 @@
    /**
    * 编辑
    * @param resources /
    * @param request /
    */
    void update(StoreQualification resources);
    void update(StoreQualificationUpdateRequest request);
    /**
     * 编辑
     * @param requests /
     */
    void batchUpdate(List<StoreQualificationUpdateRequest> requests);
    /**
    * 多选删除
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreQueryService.java
@@ -1,10 +1,15 @@
package com.oying.modules.pc.store.service;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreCustomerDetailDto;
import com.oying.modules.pc.store.domain.dto.StoreCustomerQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.utils.PageResult;
import java.util.List;
public interface StoreQueryService {
    StoreCustomerDetailDto getCustomerStoreDetail(StoreCustomerQueryCriteria criteria);
    PageResult<Store> findPagedStores(StoreQueryCriteria criteria);
}
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreService.java
@@ -3,9 +3,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.StoreCategory;
import com.oying.modules.pc.store.domain.dto.StoreBusinessHoursRequest;
import com.oying.modules.pc.store.domain.dto.StoreLocationUpdateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.modules.pc.store.domain.dto.*;
import com.oying.utils.PageResult;
import java.math.BigDecimal;
@@ -26,31 +24,35 @@
    Store getMerchantStore(Long merchantId);
    boolean create(Store store);
    List<Store> queryUserStores(Long userId);
    boolean updateLogo(Long storeId, String logo);
    boolean create(StoreCreateRequest request);
    boolean updateName(Long storeId, String storeName);
    boolean update(StoreUpdateRequest request);
    boolean updateDescription(Long storeId, String description);
    boolean updateLogo(Long storeId, String logo, Long version);
    boolean updateContactPhone(Long storeId, String contactPhone);
    boolean updateName(Long storeId, String storeName, Long version);
    boolean updateAddress(Long storeId, String address);
    boolean updateDescription(Long storeId, String description, Long version);
    boolean updateLocation(Long storeId, Double longitude, Double latitude);
    boolean updateContactPhone(Long storeId, String contactPhone, Long version);
    boolean updateRadius(Long storeId, Integer radius);
    boolean updateAddress(Long storeId, String address, Long version);
    boolean updatePlatformCategory(Long storeId, Long platformCategory);
    boolean updateLocation(Long storeId, Double longitude, Double latitude, Long version);
    boolean updateBusinessHours(Long storeId, LocalTime openTime, LocalTime endTime);
    boolean updateRadius(Long storeId, Integer radius, Long version);
    boolean updateDeliveryMinimum(Long storeId, BigDecimal deliveryMinimum);
    boolean updatePlatformCategory(Long storeId, Long platformCategory, Long version);
    boolean updateDeliveryFee(Long storeId, BigDecimal deliveryFee);
    boolean updateBusinessHours(Long storeId, LocalTime openTime, LocalTime endTime, Long version);
    boolean updateStatus(Long storeId, Integer status);
    boolean updateDeliveryMinimum(Long storeId, BigDecimal deliveryMinimum, Long version);
    boolean updateDeliveryFee(Long storeId, BigDecimal deliveryFee, Long version);
    boolean updateStatus(Long storeId, Integer status, Long version);
    boolean existsByIdAndMerchantId(Long storeId, Long merchantId);
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreCategoryServiceImpl.java
@@ -45,7 +45,6 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(StoreCategory resources) {
        // 重名
        this.calculateAndSetLevel(resources);
        storeCategoryMapper.insert(resources);
    }
@@ -68,10 +67,6 @@
     * 计算并设置类目的层级
     */
    private void calculateAndSetLevel(StoreCategory category) {
        if (category == null) {
            throw new IllegalArgumentException("对象不能为null");
        }
        Long parentCategoryId = category.getParentId();
        if (parentCategoryId == null) {
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreCreateServiceImpl.java
@@ -1,20 +1,12 @@
package com.oying.modules.pc.store.service.impl;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.dto.StoreCreateRequest;
import com.oying.modules.pc.store.service.StoreCreateService;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
@@ -22,30 +14,9 @@
public class StoreCreateServiceImpl implements StoreCreateService {
    private final StoreService storeService;
    private final StoreQualificationService storeQualificationService;
    @Transactional(rollbackFor = Exception.class)
    public void create(StoreCreateRequest request) {
        Store store = new Store();
        BeanUtils.copyProperties(request, store);
        store.setMerchantId(SecurityUtils.getCurrentUserId());
        store.setLogoImageId(request.getLogoUploadFileId());
        // store.setCoverImage(request.getCoverUploadFileId());
        storeService.create(store);
        if (request.hasQualificationList()) {
            List<StoreQualification> storeQualificationList = request.getQualificationList()
                    .stream()
                    .map(i -> {
                        StoreQualification qualification = new StoreQualification();
                        qualification.setStoreId(store.getStoreId());
                        qualification.setQualificationType(i.getType());
                        qualification.setQualificationName("");
                        qualification.setQualificationImageId(i.getImageUploadFileId());
                        return qualification;
                    })
                    .collect(Collectors.toList());
            storeQualificationList.forEach(i -> i.setStoreId(store.getStoreId()));
            storeQualificationService.batchCreate(storeQualificationList);
        }
        storeService.create(request);
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQualificationServiceImpl.java
@@ -1,13 +1,21 @@
package com.oying.modules.pc.store.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
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.google.common.collect.Lists;
import com.oying.exception.EntityExistException;
import com.oying.exception.EntityNotFoundException;
import com.oying.modules.pc.common.ValueUpdate;
import com.oying.modules.pc.store.converter.StoreQualificationAssembler;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.dto.StoreQualificationQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreQualificationUpdateRequest;
import com.oying.modules.pc.store.mapper.StoreQualificationMapper;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.service.BucketStorageService;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import lombok.RequiredArgsConstructor;
@@ -15,33 +23,49 @@
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* @description 服务实现
* @author lzp
* @date 2025-04-27
**/
 * @author lzp
 * @description 服务实现
 * @date 2025-04-27
 **/
@Service
@RequiredArgsConstructor
public class StoreQualificationServiceImpl extends ServiceImpl<StoreQualificationMapper, StoreQualification> implements StoreQualificationService {
    private final StoreQualificationMapper storeQualificationMapper;
    private final BucketStorageService bucketStorageService;
    @Override
    public PageResult<StoreQualification> queryAll(StoreQualificationQueryCriteria criteria, Page<Object> page){
    public PageResult<StoreQualification> queryAll(StoreQualificationQueryCriteria criteria, Page<Object> page) {
        return PageUtil.toPage(storeQualificationMapper.findAll(criteria, page));
    }
    @Override
    public List<StoreQualification> queryAll(StoreQualificationQueryCriteria criteria){
    public List<StoreQualification> queryAll(StoreQualificationQueryCriteria criteria) {
        return storeQualificationMapper.findAll(criteria);
    }
    @Override
    public List<StoreQualification> queryBatchIds(List<Long> ids) {
        LambdaQueryWrapper<StoreQualification> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(StoreQualification::getQualificationId, ids);
        return storeQualificationMapper.selectList(wrapper);
    }
    @Override
    public List<StoreQualification> queryByStoreId(Long storeId) {
        LambdaQueryWrapper<StoreQualification> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoreQualification::getStoreId, storeId);
        return storeQualificationMapper.selectList(wrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(StoreQualification resources) {
        // resources.setQualificationId(StoreQualificationIdGenerator.getId());
        storeQualificationMapper.insert(resources);
    }
@@ -52,20 +76,73 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(StoreQualification resources) {
        // resources.setUpdateBy();
        Long qualificationId = resources.getQualificationId();
    public void update(StoreQualificationUpdateRequest request) {
        Long qualificationId = request.getQualificationId();
        StoreQualification existingStoreQualification = this.getById(qualificationId);
        if (ObjUtil.isEmpty(existingStoreQualification)) {
            throw new EntityExistException(StoreQualification.class, "id", Optional.ofNullable(qualificationId).map(Object::toString).orElse("null"));
            throw new EntityNotFoundException(StoreQualification.class, "qualificationId", Optional.ofNullable(qualificationId).map(Object::toString).orElse("null"));
        }
        existingStoreQualification.copy(resources);
        // 新的资质数据
        StoreQualification newStoreQualification = StoreQualificationAssembler.to(request);
        // 记录图片值的变更
        ValueUpdate<Long> imageValueUpdate = new ValueUpdate<>(newStoreQualification.getQualificationImageId(), existingStoreQualification.getQualificationImageId());
        // 填充新的数据
        existingStoreQualification.copy(newStoreQualification);
        storeQualificationMapper.updateById(existingStoreQualification);
        // 删除旧图片原纪录
        if (imageValueUpdate.isChangeAndOldValueNotEmpty()) {
            bucketStorageService.deleteAll(Lists.newArrayList(imageValueUpdate.getOldValue()));
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdate(List<StoreQualificationUpdateRequest> requests) {
        List<Long> updateIds = requests.stream().map(StoreQualificationUpdateRequest::getQualificationId).collect(Collectors.toList());
        List<StoreQualification> existingStoreQualifications = this.queryBatchIds(updateIds);
        if (CollectionUtil.isEmpty(existingStoreQualifications)) {
            throw new EntityNotFoundException(StoreQualification.class, "qualificationIds", updateIds.toString());
        }
        Map<Long, StoreQualificationUpdateRequest> requestMap = requests.stream().collect(Collectors.toMap(
                StoreQualificationUpdateRequest::getQualificationId,
                storeQualificationUpdateRequest -> storeQualificationUpdateRequest
        ));
        // 提取需要删除的图片原纪录ID
        List<Long> deleteBucketStorageIds = existingStoreQualifications.stream().filter(i -> {
            Long newCloudStorageId = requestMap.get(i.getQualificationId()).getImageUploadFileId();
            return ValueUpdate.isChangeAndOldValueNotEmpty(newCloudStorageId, i.getQualificationImageId());
        }).map(StoreQualification::getQualificationImageId).collect(Collectors.toList());
        // 填充新的数据
        for (StoreQualification existingStoreQualification : existingStoreQualifications) {
            StoreQualification newStoreQualification = StoreQualificationAssembler.to(requestMap.get(existingStoreQualification.getQualificationId()));
            existingStoreQualification.copy(newStoreQualification);
        }
        this.updateBatchById(existingStoreQualifications);
        // 删除旧图片原记录
        if (CollectionUtil.isNotEmpty(deleteBucketStorageIds)) {
            bucketStorageService.deleteAll(deleteBucketStorageIds);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        List<StoreQualification> existingStoreQualifications = this.queryBatchIds(ids);
        if (CollectionUtil.isEmpty(existingStoreQualifications)) {
            throw new EntityNotFoundException(StoreQualification.class, "qualificationIds", ids.toString());
        }
        storeQualificationMapper.deleteBatchIds(ids);
        // 删除旧图片原记录
        List<Long> bucketStorageIds = existingStoreQualifications.stream().map(StoreQualification::getQualificationImageId).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(bucketStorageIds)) {
            bucketStorageService.deleteAll(bucketStorageIds);
        }
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQueryServiceImpl.java
@@ -1,33 +1,66 @@
package com.oying.modules.pc.store.service.impl;
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.StoreCustomerDetailDto;
import com.oying.modules.pc.store.domain.dto.StoreCustomerQueryCriteria;
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;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class StoreQueryServiceImpl implements StoreQueryService {
    private final RedisTemplate<Object, Object> redisTemplate;
    private final StoreService storeService;
    private final StoreMapper storeMapper;
    @Override
    public StoreCustomerDetailDto getCustomerStoreDetail(StoreCustomerQueryCriteria criteria) {
        Store store = storeService.getById(criteria.getStoreId());
        StoreCustomerDetailDto storeDto = new StoreCustomerDetailDto();
        BeanUtils.copyProperties(store, storeDto);
        storeDto.setName(store.getStoreName());
        storeDto.setLogoUrl("");
        storeDto.setBusinessHours("");
        storeDto.setDeliveryDuration(0);
        storeDto.setMonthlySales(0);
        storeDto.setScore(0);
        return storeDto;
    public PageResult<Store> findPagedStores(StoreQueryCriteria criteria) {
        PageResult<Store> page = new PageResult<>();
        // 1. 检查ZSET是否存在
        String setKey = criteria.buildConditionCacheKey();
        Long total = redisTemplate.opsForZSet().size(setKey);
        if (total == null || total == 0) {
            // 初始化缓存
            List<Long> ids = storeMapper.queryStoreIds(criteria);
            for (Long id : ids) {
                redisTemplate.opsForZSet().add(setKey, id, 1);
            }
            // 设置缓存过期时间
            redisTemplate.expire(setKey, 30, TimeUnit.MINUTES);
            total = (long) ids.size();
        }
        // 2. 从ZSET获取分页ID
        int current = (criteria.getPage() == null || criteria.getPage() < 1) ? 1 : criteria.getPage();
        int offset = (current - 1) * criteria.getSize();
        int end = offset + criteria.getSize() - 1;
        Set<Long> storeIds = Optional.ofNullable(redisTemplate.opsForZSet()
                        .range(setKey, offset, end))
                .orElse(Collections.emptySet())
                .stream().map(i-> (Long) i).collect(Collectors.toSet());
        // 3. 获取详情数据
        List<Store> stores = Optional.ofNullable(storeService.listByIds(storeIds)).orElse(ListUtil.toList());
        page.setContent(stores);
        page.setTotalElements(total);
        return page;
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreServiceImpl.java
@@ -1,24 +1,44 @@
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;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oying.exception.EntityExistException;
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;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
 * 店铺Service业务层处理
@@ -32,6 +52,9 @@
public class StoreServiceImpl extends ServiceImpl<StoreMapper, Store> implements StoreService {
    private final StoreMapper storeMapper;
    private final StoreUserMapper storeUserMapper;
    private final StoreQualificationService qualificationService;
    private final BucketStorageService bucketStorageService;
    @Override
    public PageResult<Store> queryByPage(StoreQueryCriteria criteria) {
@@ -50,92 +73,126 @@
    }
    @Override
    public boolean create(Store store) {
        // store.setStoreId(StoreIdGenerator.getId());
        return save(store);
    public List<Store> queryUserStores(Long userId) {
        return storeMapper.queryUserStores(userId);
    }
    @Override
    public boolean updateLogo(Long storeId, String logo) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId).set(Store::getLogoImageId, logo);
    @Transactional(rollbackFor = Exception.class)
    public boolean create(StoreCreateRequest request) {
        Store store = StoreAssembler.to(request);
        storeMapper.insert(store);
        this.processQualificationCreate(store, request.getQualificationList());
        this.bindUser(store.getStoreId());
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(StoreUpdateRequest request) {
        Store existingStore = this.findOrThrow(request);
        this.processImagesUpdate(request, existingStore);
        this.processQualificationUpdate(request);
        existingStore.copy(StoreAssembler.to(request));
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(existingStore.getStoreId(), existingStore.getVersion());
        return this.update(existingStore, wrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateLogo(Long storeId, String logo, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getLogoImageId, logo);
        return update(wrapper);
    }
    @Override
    public boolean updateName(Long storeId, String storeName) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateName(Long storeId, String storeName, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getStoreName, storeName);
        return update(wrapper);
    }
    @Override
    public boolean updateDescription(Long storeId, String description) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateDescription(Long storeId, String description, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getDescription, description);
        return update(wrapper);
    }
    @Override
    public boolean updateContactPhone(Long storeId, String contactPhone) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateContactPhone(Long storeId, String contactPhone, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getContactPhone, contactPhone);
        return update(wrapper);
    }
    @Override
    public boolean updateAddress(Long storeId, String address) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateAddress(Long storeId, String address, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getAddress, address);
        return update(wrapper);
    }
    @Override
    public boolean updateLocation(Long storeId, Double longitude, Double latitude) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateLocation(Long storeId, Double longitude, Double latitude, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getLongitude, longitude)
                .set(Store::getLatitude, latitude);
        return update(wrapper);
    }
    @Override
    public boolean updateRadius(Long storeId, Integer radius) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateRadius(Long storeId, Integer radius, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getRadius, radius);
        return update(wrapper);
    }
    @Override
    public boolean updatePlatformCategory(Long storeId, Long platformCategory) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updatePlatformCategory(Long storeId, Long platformCategory, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getPlatformCategoryId, platformCategory);
        return update(wrapper);
    }
    @Override
    public boolean updateBusinessHours(Long storeId, LocalTime openTime, LocalTime endTime) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateBusinessHours(Long storeId, LocalTime openTime, LocalTime endTime, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getOpenTime, openTime)
                .set(Store::getCloseTime, endTime);
        return update(wrapper);
    }
    @Override
    public boolean updateDeliveryMinimum(Long storeId, BigDecimal deliveryMinimum) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateDeliveryMinimum(Long storeId, BigDecimal deliveryMinimum, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getDeliveryMinimum, deliveryMinimum);
        return update(wrapper);
    }
    @Override
    public boolean updateDeliveryFee(Long storeId, BigDecimal deliveryFee) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateDeliveryFee(Long storeId, BigDecimal deliveryFee, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getDeliveryFee, deliveryFee);
        return update(wrapper);
    }
    @Override
    public boolean updateStatus(Long storeId, Integer status) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId)
    public boolean updateStatus(Long storeId, Integer status, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getStatus, status);
        return update(wrapper);
    }
@@ -149,9 +206,83 @@
        return Optional.ofNullable(store).map(i -> merchantId.equals(i.getMerchantId())).orElse(false);
    }
    private LambdaUpdateWrapper<Store> createLambdaUpdateWrapper(Long storeId) {
        return new LambdaUpdateWrapper<Store>()
                .eq(Store::getStoreId, storeId)
                .set(Store::getUpdateBy, SecurityUtils.getCurrentUserId());
    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) {
        StoreUser storeUser = new StoreUser();
        storeUser.setStoreId(storeId);
        storeUser.setUserId(SecurityUtils.getCurrentUserId());
        storeUser.setRoleType("");
        storeUser.setPermissions("");
        storeUserMapper.insert(storeUser);
    }
    private LambdaUpdateWrapper<Store> createLambdaUpdateWrapper(Long storeId, Long version) {
        Objects.requireNonNull(storeId, String.format("方法 '%s' 调用失败:参数 'storeId'(店铺ID)不能为 null", "createLambdaUpdateWrapper"));
        LambdaUpdateWrapper<Store> updateWrapper =  new LambdaUpdateWrapper<>();
        updateWrapper.eq(Store::getStoreId, storeId);
        if (ObjUtil.isNotEmpty(version)) {
            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/java/com/oying/modules/pc/store/view/CustomerStoreQualificationView.java
@@ -8,7 +8,6 @@
@Data
public class CustomerStoreQualificationView {
    @NotNull
    @ApiModelProperty(value = "资质类型")
    private Integer type;
@@ -18,4 +17,13 @@
    @ApiModelProperty(value = "资质图片")
    private String imageUrl;
    @ApiModelProperty(value = "资质类型")
    private Integer qualificationType;
    @ApiModelProperty(value = "资质名称")
    private String qualificationName;
    @ApiModelProperty(value = "资质图片")
    private String qualificationImageUrl;
}
oying-system/src/main/java/com/oying/modules/pc/store/view/CustomerStoreView.java
@@ -1,28 +1,79 @@
package com.oying.modules.pc.store.view;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalTime;
@Data
public class CustomerStoreView {
    private String name;
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    private String logoUrl;
    @ApiModelProperty(value = "商户ID")
    private Long merchantId;
    @ApiModelProperty(value = "平台类目")
    private Long platformCategoryId;
    @ApiModelProperty(value = "店铺类型:1-自营 2-加盟 3-第三方")
    private Integer storeType;
    @ApiModelProperty(value = "店铺名称")
    private String storeName;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "店铺logo图片Url")
    private String logoImageUrl;
    @ApiModelProperty(value = "店铺封面图Url")
    private String coverImageUrl;
    @ApiModelProperty(value = "店铺描述")
    private String description;
    private String address;
    @ApiModelProperty(value = "联系电话")
    private String contactPhone;
    @ApiModelProperty(value = "营业开始时间")
    private LocalTime openTime;
    @ApiModelProperty(value = "营业结束时间")
    private LocalTime closeTime;
    private String businessHours;
    private String contactPhone;
    @ApiModelProperty(value = "详细地址")
    private String address;
    private Integer score;
    @ApiModelProperty(value = "经度")
    private Double longitude;
    private Integer deliveryDuration;
    @ApiModelProperty(value = "纬度")
    private Double latitude;
    private Integer monthlySales;
    @ApiModelProperty(value = "营业半径(米)")
    private Integer radius;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "评分")
    private Double score = 0d;
    @ApiModelProperty(value = "配送距离")
    private Integer deliveryDuration = 0;
    @ApiModelProperty(value = "月销售量")
    private Integer monthlySales = 0;
}
oying-system/src/main/java/com/oying/modules/pc/store/view/StoreMerchantView.java
@@ -17,7 +17,20 @@
    @ApiModelProperty(value = "店铺名称")
    private String storeName;
    private String logoUrl;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "店铺logo图片")
    private Long logoImageId;
    @ApiModelProperty(value = "店铺logo图片Url")
    private String logoImageUrl;
    @ApiModelProperty(value = "店铺封面图")
    private Long coverImageId;
    @ApiModelProperty(value = "店铺封面图Url")
    private String coverImageUrl;
    @ApiModelProperty(value = "店铺描述")
    private String description;
@@ -43,6 +56,13 @@
    @ApiModelProperty(value = "营业半径(米)")
    private Integer radius;
    private Integer status;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @ApiModelProperty(value = "版本号")
    private Long version;
}
oying-system/src/main/java/com/oying/modules/pc/store/view/StoreQualificationMerchantView.java
@@ -1,9 +1,12 @@
package com.oying.modules.pc.store.view;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalTime;
@Data
public class StoreQualificationMerchantView {
@@ -21,4 +24,25 @@
    @ApiModelProperty(value = "资质图片")
    private String imageUrl;
    @ApiModelProperty(value = "关联的店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "资质ID")
    private Long qualificationId;
    @ApiModelProperty(value = "资质类型")
    private Integer qualificationType;
    @ApiModelProperty(value = "资质名称")
    private String qualificationName;
    @ApiModelProperty(value = "资质图片")
    private Long qualificationImageId;
    @ApiModelProperty(value = "资质图片")
    private String qualificationImageUrl;
    @ApiModelProperty(value = "状态")
    private Integer status;
}
oying-system/src/main/java/com/oying/modules/pc/store/view/StoreSimpleView.java
@@ -1,16 +1,81 @@
package com.oying.modules.pc.store.view;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalTime;
@Data
public class StoreSimpleView {
    private Long id;
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    private String name;
    @ApiModelProperty(value = "商户ID")
    private Long merchantId;
    private String logoUrl;
    @ApiModelProperty(value = "平台类目")
    private Long platformCategoryId;
    @ApiModelProperty(value = "店铺类型:1-自营 2-加盟 3-第三方")
    private Integer storeType;
    @ApiModelProperty(value = "店铺名称")
    private String storeName;
    @ApiModelProperty(value = "状态")
    private Integer status;
    @ApiModelProperty(value = "店铺logo图片")
    private Long logoImageId;
    @ApiModelProperty(value = "店铺logo图片Url")
    private String logoImageUrl;
    @ApiModelProperty(value = "店铺封面图")
    private Long coverImageId;
    @ApiModelProperty(value = "店铺封面图Url")
    private String coverImageUrl;
    @ApiModelProperty(value = "店铺描述")
    private String description;
    @ApiModelProperty(value = "配送费用")
    private BigDecimal deliveryFee;
    @ApiModelProperty(value = "起送金额")
    private BigDecimal deliveryMinimum;
    @ApiModelProperty(value = "联系电话")
    private String contactPhone;
    @ApiModelProperty(value = "营业开始时间")
    private LocalTime openTime;
    @ApiModelProperty(value = "营业结束时间")
    private LocalTime closeTime;
    @ApiModelProperty(value = "市级代码")
    private String cityCode;
    @ApiModelProperty(value = "详细地址")
    private String address;
    @ApiModelProperty(value = "经度")
    private Double longitude;
    @ApiModelProperty(value = "纬度")
    private Double latitude;
    @ApiModelProperty(value = "营业半径(米)")
    private Integer radius;
    @ApiModelProperty(value = "是否支持退货")
    private Integer returns;
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
}
oying-system/src/main/java/com/oying/modules/pc/utils/BusinessHoursUtils.java
New file
@@ -0,0 +1,42 @@
package com.oying.modules.pc.utils;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.format.DateTimeFormatter;
public class BusinessHoursUtils {
    // 时间格式
    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
    /**
     * 拼接营业时间,如果时间为 null,则返回 "Closed"
     *
     * @param openTime  开始时间
     * @param closeTime 结束时间
     * @return 格式化的营业时间字符串,如 "8:00-22:00"
     */
    public static String formatBusinessHours(LocalTime openTime, LocalTime closeTime) {
        if (openTime == null || closeTime == null) {
            return "Closed";
        }
        return openTime.format(DEFAULT_FORMATTER) + "-" + closeTime.format(DEFAULT_FORMATTER);
    }
    // 示例调用
    public static void main(String[] args) {
        // LocalDateTime localDateTime = LocalDateTime.of(2025, Month.MAY, 10, 8, 0, 0);
        // LocalDateTime localDateTime2 = LocalDateTime.of(2025, Month.MAY, 10, 22, 0, 0);
        LocalTime openTime = LocalTime.of(8, 0);
        LocalTime closeTime =  LocalTime.of(22, 0);
        // 正常情况 → "8:00-22:00"
        System.out.println(formatBusinessHours(openTime, closeTime));
        // 如果时间为 null → "Closed"
        System.out.println(formatBusinessHours(null, null));
    }
}
oying-system/src/main/java/com/oying/modules/pc/utils/ImageUtils.java
New file
@@ -0,0 +1,31 @@
package com.oying.modules.pc.utils;
import com.oying.domain.BucketStorage;
import com.oying.exception.EntityNotFoundException;
import com.oying.service.BucketStorageService;
import com.oying.utils.ObsProperties;
import com.oying.utils.ObsUtils;
import com.oying.utils.SpringBeanHolder;
import java.util.Optional;
public class ImageUtils {
    private final static ObsProperties properties = SpringBeanHolder.getBean(ObsProperties.class);
    private final static BucketStorageService bucketStorageService = SpringBeanHolder.getBean(BucketStorageService.class);
    public static String getPublicObjectUrl(String path) {
        return ObsUtils.getPublicObjectUrl(properties, path);
    }
    public static String getPublicObjectUrl(Long storageId) {
        return Optional.ofNullable(storageId).map(v -> {
            BucketStorage bucketStorage = bucketStorageService.getById(storageId);
            String path = Optional.ofNullable(bucketStorage)
                    .map(BucketStorage::getPath)
                    .orElseThrow(() -> new EntityNotFoundException(BucketStorage.class, "bucketStorageId", storageId.toString()));
            return getPublicObjectUrl(path);
        }).orElse(null);
    }
}
oying-system/src/main/resources/mapper/message/MessageOrderBuyerMapper.xml
@@ -8,10 +8,13 @@
        <result column="message_content" property="messageContent"/>
        <result column="link" property="link"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <result column="create_by" property="createBy"/>
        <result column="update_by" property="updateBy"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, order_id, message_type, message_content, link, create_time
        id, order_id, message_type, message_content, link, create_time, update_time, create_by, update_by
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
oying-system/src/main/resources/mapper/message/MessageOrderLeaveMapper.xml
@@ -8,10 +8,13 @@
        <result column="seller_reply" property="sellerReply"/>
        <result column="link" property="link"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <result column="create_by" property="createBy"/>
        <result column="update_by" property="updateBy"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, order_id, customer_review, seller_reply, link, create_time
        id, order_id, customer_review, seller_reply, link, create_time, update_time, create_by, update_by
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
oying-system/src/main/resources/mapper/message/MessageOrderSellerMapper.xml
@@ -8,10 +8,13 @@
        <result column="message_content" property="messageContent"/>
        <result column="link" property="link"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <result column="create_by" property="createBy"/>
        <result column="update_by" property="updateBy"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, order_id, message_type, message_content, link, create_time
        id, order_id, message_type, message_content, link, create_time, update_time, create_by, update_by
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
oying-system/src/main/resources/mapper/message/MessageSystemAdvertiseMapper.xml
New file
@@ -0,0 +1,29 @@
<?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.message.mapper.MessageSystemAdvertiseMapper">
    <resultMap id="BaseResultMap" type="com.oying.modules.message.domain.MessageSystemAdvertise">
        <id column="id" property="id"/>
        <result column="title" property="title"/>
        <result column="content" property="content"/>
        <result column="image_url" property="imageUrl"/>
        <result column="link_url" property="linkUrl"/>
        <result column="status" property="status"/>
        <result column="create_by" property="createBy"/>
        <result column="update_by" property="updateBy"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, title, content, image_url, link_url, status, create_by, update_by, create_time, update_time
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from message_system_advertise
        <where>
        </where>
        order by id desc
    </select>
</mapper>
oying-system/src/main/resources/mapper/message/MessageSystemMapper.xml
@@ -5,10 +5,14 @@
        <id column="id" property="id"/>
        <result column="start_time" property="startTime"/>
        <result column="end_time" property="endTime"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <result column="create_by" property="createBy"/>
        <result column="update_by" property="updateBy"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, start_time, end_time
        id, start_time, end_time, create_time, update_time, create_by, update_by
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
oying-system/src/main/resources/mapper/pc/product/ProductImageMapper.xml
New file
@@ -0,0 +1,30 @@
<?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.ProductImageMapper">
    <resultMap id="BaseResultMap" type="com.oying.modules.pc.product.domain.ProductImage">
        <id column="image_id" property="imageId"/>
        <result column="product_id" property="productId"/>
        <result column="cloud_storage_id" property="cloudStorageId"/>
        <result column="image_type" property="imageType"/>
        <result column="image_url" property="imageUrl"/>
        <result column="sort_weight" property="sortWeight"/>
        <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">
        image_id, product_id, cloud_storage_id, image_type, image_url, sort_weight, create_by, create_time, update_by, update_time
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from pc_product_image
        <where>
        </where>
        order by image_id desc
    </select>
</mapper>
oying-system/src/main/resources/mapper/pc/product/ProductLabelMapper.xml
New file
@@ -0,0 +1,28 @@
<?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.ProductLabelMapper">
    <resultMap id="BaseResultMap" type="com.oying.modules.pc.product.domain.ProductLabel">
        <id column="label_id" property="labelId"/>
        <result column="product_id" property="productId"/>
        <result column="category_name" property="categoryName"/>
        <result column="label_name" property="labelName"/>
        <result column="label_value" property="labelValue"/>
        <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">
        label_id, product_id, category_name, label_name, label_value, create_by, create_time, update_by, update_time
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from pc_product_label
        <where>
        </where>
        order by label_id desc
    </select>
</mapper>
oying-system/src/main/resources/mapper/pc/product/ProductMapper.xml
@@ -11,8 +11,8 @@
        <result column="category_id" property="categoryId"/>
        <result column="second_category_id" property="secondCategoryId"/>
        <result column="status" property="status"/>
        <result column="main_image" property="mainImage"/>
        <result column="detail_image" property="detailImage"/>
        <result column="main_image_id" property="mainImageId"/>
        <result column="main_image_Url" property="mainImageUrl"/>
        <result column="description" property="description"/>
        <result column="price" property="price"/>
        <result column="stock_quantity" property="stockQuantity"/>
@@ -22,12 +22,18 @@
        <result column="width" property="width"/>
        <result column="length" property="length"/>
        <result column="height" property="height"/>
        <result column="returns" property="returns"/>
        <result column="self_pickup" property="selfPickup"/>
        <result column="deleted_flag" property="deletedFlag"/>
        <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"/>
    </resultMap>
    <sql id="Base_Column_List">
        product_id, store_id, code, barcode, name, title, category_id, second_category_id, status, main_image, detail_image, description, price, stock_quantity, min_purchase_quantity, warn_stock, weight, width, length, height, deleted_flag, create_by, create_time, update_by, update_time, version
        product_id, store_id, code, barcode, name, title, category_id, second_category_id, status, main_image_id, main_image_url, description, price, stock_quantity, min_purchase_quantity, warn_stock, weight, width, length, height, returns, self_pickup, deleted_flag, create_by, create_time, update_by, update_time, version
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
@@ -55,6 +61,9 @@
            </if>
        </where>
        order by product_id desc
        <if test="criteria.limit != null">
            limit #{criteria.limit}
        </if>
    </select>
</mapper>
oying-system/src/main/resources/mapper/pc/store/StoreCategoryMapper.xml
@@ -25,9 +25,15 @@
        <include refid="Base_Column_List"/>
        from pc_store_category
        <where>
            <if test="criteria.categoryId != null ">
                and category_id = #{criteria.categoryId}
            </if>
            <if test="criteria.storeId != null ">
                and store_id = #{criteria.storeId}
            </if>
            <if test="criteria.blurry != null and criteria.blurry != ''">
                and name like concat('%',#{criteria.blurry},'%')
            </if>
            <if test="criteria.level != null ">
                and level = #{criteria.level}
            </if>
oying-system/src/main/resources/mapper/pc/store/StoreMapper.xml
@@ -28,23 +28,26 @@
        <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="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"/>
    </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,
        address, longitude, latitude, geo_hash, geo_point, coordinate_system, radius, create_by, create_time, update_by, update_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,
        address, longitude, latitude, geo_hash, geo_point, coordinate_system, radius, create_by, create_time, update_by, update_time from pc_store
        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>
    <select id="selectStoreList" resultMap="StoreResult">
@@ -105,4 +108,40 @@
        </where>
    </select>
    <select id="queryStoreIds" resultType="java.lang.Long">
        SELECT s.store_id AS storeId
        FROM pc_store s
        <where>
            <!-- 位置条件 -->
            <if test="criteria.longitude != null and criteria.latitude != null and criteria.radius != null">
                AND ST_Distance_Sphere(POINT(#{criteria.longitude}, #{criteria.latitude}), POINT(s.longitude,
                s.latitude)) &lt;= #{criteria.radius}
                AND ST_Distance_Sphere(POINT(#{criteria.longitude}, #{criteria.latitude}), POINT(s.longitude,
                s.latitude)) &lt;= s.radius
            </if>
            <!-- 店铺名称模糊查询 AND s.name LIKE CONCAT('%', #{criteria.blurry}, '%') -->
            <if test="criteria.blurry != null and criteria.blurry != ''">
                AND MATCH(s.store_name) AGAINST(#{criteria.blurry} IN NATURAL LANGUAGE MODE)
            </if>
            <!-- 营业状态 -->
            <if test="criteria.status != null">
                AND s.status = #{criteria.status}
            </if>
            <!-- 类目ID -->
            <if test="criteria.platformCategoryId != null">
                AND s.platform_category_id = #{criteria.platformCategoryId}
            </if>
        </where>
        LIMIT 1000
    </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
        WHERE u.user_id = #{userId}
    </select>
</mapper>
oying-system/src/main/resources/mapper/pc/store/StoreQualificationMapper.xml
@@ -8,6 +8,7 @@
        <result column="qualification_number" property="qualificationNumber"/>
        <result column="qualification_name" property="qualificationName"/>
        <result column="qualification_image_id" property="qualificationImageId"/>
        <result column="qualification_image_url" property="qualificationImageUrl"/>
        <result column="start_date" property="startDate"/>
        <result column="end_date" property="endDate"/>
        <result column="status" property="status"/>
@@ -18,8 +19,8 @@
    </resultMap>
    <sql id="Base_Column_List">
        qualification_id, store_id, qualification_type, qualification_number, qualification_name, qualification_image_id, start_date, end_date, status,
        create_by, create_time, update_by, update_time
        qualification_id, store_id, qualification_type, qualification_number, qualification_name, qualification_image_id, qualification_image_url,
        start_date, end_date, status, create_by, create_time, update_by, update_time
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
@@ -27,6 +28,9 @@
        <include refid="Base_Column_List"/>
        from pc_store_qualification
        <where>
            <if test="criteria.qualificationId != null">
                and qualification_id = #{criteria.qualificationId}
            </if>
            <if test="criteria.storeId != null">
                and store_id = #{criteria.storeId}
            </if>
oying-tools/src/main/java/com/oying/utils/ObsUtils.java
@@ -124,4 +124,15 @@
        // 删除指定的对象
        obsClient.deleteObject(properties.getBucket(), objectKey);
    }
    public static String getPublicObjectUrl(ObsProperties properties, String objectKey) {
        return getPublicObjectUrl(properties.getBucket(), properties.getEndpoint(), objectKey);
    }
    public static String getPublicObjectUrl(String bucketName, String endpoint, String objectKey) {
        // 移除objectKey开头可能存在的文件分割符('/')
        String cleanObjectKey = objectKey.startsWith("/") ? objectKey.substring(1) : objectKey;
        // 标准URL格式
        return String.format("https://%s.%s/%s", bucketName, endpoint.replaceAll("^https?://", ""), cleanObjectKey);
    }
}