1 files added
15 files modified
| | |
| | | config.setDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | // 开启引用检测,枚举支持 |
| | | config.setWriterFeatures( |
| | | JSONWriter.Feature.WriteLongAsString, |
| | | JSONWriter.Feature.WriteEnumUsingToString, |
| | | JSONWriter.Feature.ReferenceDetection |
| | | ); |
| | |
| | | package com.oying.exception.handler; |
| | | |
| | | import com.oying.exception.BadRequestException; |
| | | import com.oying.exception.EntityExistException; |
| | | import com.oying.exception.EntityNotFoundException; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import com.oying.exception.BadRequestException; |
| | | import com.oying.utils.DuplicateKeyExceptionUtil; |
| | | import com.oying.utils.ThrowableUtil; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.dao.DuplicateKeyException; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.security.authentication.BadCredentialsException; |
| | | import org.springframework.validation.FieldError; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 处理 DuplicateKeyException |
| | | */ |
| | | @ExceptionHandler(value = DuplicateKeyException.class) |
| | | public ResponseEntity<ApiError> handleDuplicateKeyException(DuplicateKeyException e) { |
| | | // 打印堆栈信息 |
| | | log.error(ThrowableUtil.getStackTrace(e)); |
| | | return buildResponseEntity(ApiError.error(BAD_REQUEST.value(), DuplicateKeyExceptionUtil.getDisplayMessage(e))); |
| | | } |
| | | |
| | | /** |
| | | * 统一返回 |
| | | */ |
| | | private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) { |
New file |
| | |
| | | package com.oying.utils; |
| | | |
| | | import cn.hutool.core.map.MapUtil; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.dao.DuplicateKeyException; |
| | | |
| | | import java.sql.SQLException; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | @Slf4j |
| | | public class DuplicateKeyExceptionUtil { |
| | | |
| | | public static final Map<String, String> INDEX_MAPPING = MapUtil.ofEntries( |
| | | MapUtil.entry("pc_store.uk_store_name", "店铺名称") |
| | | ); |
| | | |
| | | /** |
| | | * 获取错误消息 |
| | | */ |
| | | public static String getDisplayMessage(DuplicateKeyException e) { |
| | | try { |
| | | String indexName = findMySQLDuplicateKey(e); |
| | | String fieldDisplayName = INDEX_MAPPING.getOrDefault(indexName, indexName); |
| | | return Optional.ofNullable(fieldDisplayName).map(o -> o + "已存在").orElse(e.getMessage()); |
| | | } catch (Exception ex) { |
| | | log.error("解析'DuplicateKeyException'消息失败", ex); |
| | | return e.getMessage(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 提取索引名称 |
| | | */ |
| | | public static String findMySQLDuplicateKey(DuplicateKeyException e) { |
| | | if (e.getCause() instanceof SQLException) { |
| | | SQLException sqlEx = (SQLException) e.getCause(); |
| | | if (sqlEx.getErrorCode() == 1062) { // MySQL duplicate key error code |
| | | String errorMsg = sqlEx.getMessage(); |
| | | // 使用正则提取索引名 |
| | | Matcher matcher = Pattern.compile("for key '(.+?)'").matcher(errorMsg); |
| | | if (matcher.find()) { |
| | | return matcher.group(1); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | } |
| | |
| | | |
| | | private Integer sortWeight; |
| | | |
| | | private Long iconId; |
| | | |
| | | private String iconUrl; |
| | | |
| | | private Integer active; |
| | |
| | | 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.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.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | |
| | | // @PreAuthorize("@el.check('product:list')") |
| | | public ResponseEntity<?> getProductsByPage(ProductQueryCriteria criteria) { |
| | | Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize()); |
| | | return ResponseEntity.ok(R.success(productService.queryAll(criteria, page))); |
| | | PageResult<Product> pageResult = productService.queryAll(criteria, page); |
| | | pageResult.getContent().forEach(product -> { |
| | | Long productId = product.getProductId(); |
| | | product.setImages(productImageService.queryImagesByProductId(productId)); |
| | | product.setLabels(productLabelService.queryLabelsByProductId(productId)); |
| | | }); |
| | | return ResponseEntity.ok(R.success(pageResult)); |
| | | } |
| | | |
| | | @GetMapping(value = "/{productId}}") |
| | |
| | | //@PreAuthorize("@el.check('merchant:product:add')") |
| | | public ResponseEntity<?> createProduct(@Validated @RequestBody ProductMerchantCreateRequest request) { |
| | | productAdminService.create(request); |
| | | return ResponseEntity.noContent().build(); |
| | | return ResponseEntity.status(HttpStatus.CREATED).build(); |
| | | } |
| | | |
| | | @PutMapping |
| | |
| | | package com.oying.modules.pc.store.domain.dto; |
| | | |
| | | import com.oying.utils.StringUtils; |
| | | import lombok.Data; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import org.springframework.util.DigestUtils; |
| | | |
| | | import java.io.Serializable; |
| | |
| | | @ApiModelProperty(value = "商户ID", example = "1") |
| | | private Long merchantId; |
| | | |
| | | private String storeName; |
| | | |
| | | private Integer status; |
| | | |
| | | private Long storeId; |
| | | |
| | | private Long platformCategoryId; |
| | | |
| | | private String storeName; |
| | | |
| | | private Integer status; |
| | | |
| | | private String blurry; |
| | | |
| | |
| | | |
| | | 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); |
| | | } |
| | | baseKeyJoiner.add("merchantId=" + merchantId); |
| | | baseKeyJoiner.add("storeId=" + storeId); |
| | | baseKeyJoiner.add("platformCategoryId=" + platformCategoryId); |
| | | baseKeyJoiner.add("storeName=" + storeName); |
| | | baseKeyJoiner.add("status=" + status); |
| | | baseKeyJoiner.add("blurry=" + blurry); |
| | | baseKeyJoiner.add("longitude=" + longitude); |
| | | baseKeyJoiner.add("latitude=" + latitude); |
| | | baseKeyJoiner.add("radius=" + radius); |
| | | // 使用MD5或SHA缩短键长度 |
| | | return "store:search:page:" + DigestUtils.md5DigestAsHex(baseKeyJoiner.toString().getBytes()); |
| | | } |
| | |
| | | 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.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 com.oying.modules.pc.store.domain.Store; |
| | | import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | |
| | | @Mapper |
| | | public interface StoreMapper extends BaseMapper<Store> { |
| | | |
| | | List<Store> selectStoreList(@Param("criteria") StoreQueryCriteria criteria, Page<Store> page); |
| | | IPage<Store> selectStoreList(@Param("criteria") StoreQueryCriteria criteria, Page<Store> page); |
| | | |
| | | List<Store> selectStoreList(@Param("criteria") StoreQueryCriteria criteria); |
| | | |
| | |
| | | @PostMapping |
| | | @ApiOperation("创建店铺") |
| | | public ResponseEntity<?> create(@RequestBody StoreCreateRequest request) { |
| | | storeService.create(request); |
| | | return ResponseEntity.status(HttpStatus.CREATED).build(); |
| | | return ResponseEntity.status(HttpStatus.CREATED).body(R.success(storeService.create(request))); |
| | | } |
| | | |
| | | @PostMapping(value = "/{storeId}") |
| | |
| | | @GetMapping(value = "/page") |
| | | @ApiOperation("查询店铺") |
| | | public ResponseEntity<?> getStoresByPage(StoreQueryCriteria criteria) { |
| | | criteria.setLimit(1000); |
| | | PageResult<Store> pagedStores = storeQueryService.findPagedStores(criteria); |
| | | List<Store> stores = pagedStores.getContent(); |
| | | for (Store store : stores) { |
| | | store.setProducts(this.getProductsByStoreId(store.getStoreId())); |
| | | } |
| | | pagedStores.getContent().forEach(store -> store.setProducts(this.getProductsByStoreId(store.getStoreId()))); |
| | | return ResponseEntity.ok(R.success(pagedStores)); |
| | | } |
| | | |
| | |
| | | @ApiOperation("创建店铺") |
| | | //@PreAuthorize("@el.check('merchant:store:create')") |
| | | public ResponseEntity<?> create(@RequestBody StoreCreateRequest request) { |
| | | storeCreateService.create(request); |
| | | return ResponseEntity.status(HttpStatus.CREATED).build(); |
| | | return ResponseEntity.status(HttpStatus.CREATED).body(storeCreateService.create(request)); |
| | | } |
| | | |
| | | /** |
| | |
| | | package com.oying.modules.pc.store.service; |
| | | |
| | | import com.oying.modules.pc.store.domain.Store; |
| | | import com.oying.modules.pc.store.domain.dto.StoreCreateRequest; |
| | | |
| | | public interface StoreCreateService { |
| | | void create(StoreCreateRequest request); |
| | | Store create(StoreCreateRequest request); |
| | | } |
| | |
| | | |
| | | 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.*; |
| | | 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.utils.PageResult; |
| | | |
| | | import java.math.BigDecimal; |
| | |
| | | |
| | | List<Store> queryUserStores(Long userId); |
| | | |
| | | boolean create(StoreCreateRequest request); |
| | | Store create(StoreCreateRequest request); |
| | | |
| | | boolean update(StoreUpdateRequest request); |
| | | |
| | |
| | | boolean updateStatus(Long storeId, Integer status, Long version); |
| | | |
| | | boolean existsByIdAndMerchantId(Long storeId, Long merchantId); |
| | | |
| | | boolean existsStoreName(String storeName); |
| | | } |
| | |
| | | package com.oying.modules.pc.store.service.impl; |
| | | |
| | | import com.oying.modules.pc.store.domain.Store; |
| | | import com.oying.modules.pc.store.domain.dto.StoreCreateRequest; |
| | | import com.oying.modules.pc.store.service.StoreCreateService; |
| | | import com.oying.modules.pc.store.service.StoreService; |
| | |
| | | private final StoreService storeService; |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void create(StoreCreateRequest request) { |
| | | storeService.create(request); |
| | | public Store create(StoreCreateRequest request) { |
| | | return storeService.create(request); |
| | | } |
| | | } |
| | |
| | | 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.BadRequestException; |
| | | import com.oying.exception.EntityNotFoundException; |
| | | import com.oying.modules.pc.common.ValueUpdate; |
| | | import com.oying.modules.pc.store.converter.StoreAssembler; |
| | |
| | | import com.oying.utils.PageResult; |
| | | import com.oying.utils.PageUtil; |
| | | import com.oying.utils.SecurityUtils; |
| | | import com.oying.utils.StringUtils; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean create(StoreCreateRequest request) { |
| | | public Store create(StoreCreateRequest request) { |
| | | // 检查店铺名称是否存在 |
| | | if (this.existsStoreName(request.getStoreName())) { |
| | | throw new BadRequestException("店铺名称已存在"); |
| | | } |
| | | // 创建店铺 |
| | | Store store = StoreAssembler.to(request); |
| | | storeMapper.insert(store); |
| | | this.processQualificationCreate(store, request.getQualificationList()); |
| | | this.bindUser(store.getStoreId()); |
| | | return true; |
| | | return store; |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | @Override |
| | | public boolean existsStoreName(String storeName) { |
| | | if (StringUtils.isEmpty(storeName)) { |
| | | return true; |
| | | } |
| | | LambdaQueryWrapper<Store> wrapper = new LambdaQueryWrapper<Store>() |
| | | .eq(Store::getStoreName, storeName); |
| | | return storeMapper.selectCount(wrapper) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public boolean existsByIdAndMerchantId(Long storeId, Long merchantId) { |
| | | if (storeId == null || merchantId == null) { |
| | | return false; |
| | |
| | | <result column="level" property="level"/> |
| | | <result column="sort_weight" property="sortWeight"/> |
| | | <result column="icon_id" property="iconId"/> |
| | | <result column="icon_url" property="iconId"/> |
| | | <result column="icon_url" property="iconUrl"/> |
| | | <result column="status" property="status"/> |
| | | <result column="active" property="active"/> |
| | | <result column="create_by" property="createBy"/> |
| | |
| | | </resultMap> |
| | | |
| | | <sql id="Base_Column_List"> |
| | | category_id, parent_id, name, level, sort_weight, icon_id, status, active, create_by, create_time, update_by, update_time |
| | | category_id, parent_id, name, level, sort_weight, icon_id, icon_url, status, active, create_by, create_time, update_by, update_time |
| | | </sql> |
| | | |
| | | <select id="findAll" resultMap="BaseResultMap"> |
| | |
| | | AND s.platform_category_id = #{criteria.platformCategoryId} |
| | | </if> |
| | | </where> |
| | | LIMIT 1000 |
| | | <if test="criteria.limit != null"> |
| | | limit #{criteria.limit} |
| | | </if> |
| | | </select> |
| | | |
| | | <select id="queryUserStores" parameterType="java.lang.Long" resultMap="StoreResult"> |