diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index 8b0f69996..1bb8863ba 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -99,3 +99,57 @@ COMMENT ON COLUMN notify_message.version IS '版本号(乐观锁)'; COMMENT ON COLUMN notify_message.deleted IS '逻辑删除标志'; CREATE INDEX IF NOT EXISTS idx_notify_message_user ON notify_message (user_id, deleted, is_read); + +-- =================================== +-- 商户门店(商户物理经营场所) +-- =================================== +CREATE TABLE IF NOT EXISTS mch_store_info ( + id bigint NOT NULL, + mch_no varchar(32) NOT NULL, + store_no varchar(32) NOT NULL, + store_name varchar(128) NOT NULL, + contact_phone varchar(32), + logo_url varchar(512), + facade_url varchar(512), + interior_url varchar(512), + region_code varchar(12), + address varchar(256), + longitude numeric(10,7), + latitude numeric(10,7), + status varchar(16) NOT NULL DEFAULT 'enable', + remark varchar(512), + creator bigint, + create_time timestamptz(6), + last_modifier bigint, + last_modified_time timestamptz(6), + version int NOT NULL DEFAULT 0, + deleted boolean NOT NULL DEFAULT false, + CONSTRAINT mch_store_info_pkey PRIMARY KEY (id) +); + +COMMENT ON TABLE mch_store_info IS '商户门店(商户物理经营场所)'; +COMMENT ON COLUMN mch_store_info.id IS '主键'; +COMMENT ON COLUMN mch_store_info.mch_no IS '商户号'; +COMMENT ON COLUMN mch_store_info.store_no IS '门店号(系统生成, 唯一)'; +COMMENT ON COLUMN mch_store_info.store_name IS '门店名称'; +COMMENT ON COLUMN mch_store_info.contact_phone IS '联系人电话'; +COMMENT ON COLUMN mch_store_info.logo_url IS '门店LOGO'; +COMMENT ON COLUMN mch_store_info.facade_url IS '门头照'; +COMMENT ON COLUMN mch_store_info.interior_url IS '门店内景照'; +COMMENT ON COLUMN mch_store_info.region_code IS '行政区划代码(区县级)'; +COMMENT ON COLUMN mch_store_info.address IS '详细地址'; +COMMENT ON COLUMN mch_store_info.longitude IS '经度'; +COMMENT ON COLUMN mch_store_info.latitude IS '纬度'; +COMMENT ON COLUMN mch_store_info.status IS '状态(enable启用/disabled停用)'; +COMMENT ON COLUMN mch_store_info.remark IS '备注'; +COMMENT ON COLUMN mch_store_info.creator IS '创建人ID'; +COMMENT ON COLUMN mch_store_info.create_time IS '创建时间'; +COMMENT ON COLUMN mch_store_info.last_modifier IS '最后修改人ID'; +COMMENT ON COLUMN mch_store_info.last_modified_time IS '最后修改时间'; +COMMENT ON COLUMN mch_store_info.version IS '版本号(乐观锁)'; +COMMENT ON COLUMN mch_store_info.deleted IS '逻辑删除标志'; + +-- 门店号唯一索引 +CREATE UNIQUE INDEX IF NOT EXISTS uk_mch_store_info_store_no ON mch_store_info (store_no); +-- 商户号查询索引 +CREATE INDEX IF NOT EXISTS idx_mch_store_info_mch_no ON mch_store_info (mch_no, deleted); diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/merchant/store/MchStoreInfoAdminController.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/merchant/store/MchStoreInfoAdminController.java new file mode 100644 index 000000000..be712cd73 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/merchant/store/MchStoreInfoAdminController.java @@ -0,0 +1,70 @@ +package cn.daxpay.open.payment.admin.controller.merchant.store; + +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoParam; +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoQuery; +import cn.daxpay.open.payment.merchant.result.store.MchStoreInfoResult; +import cn.daxpay.open.payment.merchant.service.store.MchStoreInfoService; +import cn.daxpay.open.platform.core.annotation.PermCode; +import cn.daxpay.open.platform.core.rest.Res; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.core.rest.result.PageResult; +import cn.daxpay.open.platform.core.rest.result.Result; +import cn.daxpay.open.platform.core.util.ValidationUtil; +import cn.daxpay.open.platform.core.validation.ValidationGroup; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/// 门店信息管理(管理端) +@PermCode(menuCode = "payment:merchant:store") +@Validated +@Tag(name = "门店信息管理(管理端)") +@RestController +@RequestMapping("/admin/mch/store") +@RequiredArgsConstructor +public class MchStoreInfoAdminController { + private final MchStoreInfoService mchStoreInfoService; + + @PermCode(code = "add", nameCn = "门店新增", nameEn = "Store Add") + @Operation(summary = "新增门店") + @PostMapping("/add") + public Result add(@RequestBody @Validated(ValidationGroup.add.class) MchStoreInfoParam param) { + ValidationUtil.validateParam(param, ValidationGroup.add.class); + mchStoreInfoService.add(param); + return Res.ok(); + } + + @PermCode(code = "edit", nameCn = "门店编辑", nameEn = "Store Edit") + @Operation(summary = "修改门店") + @PostMapping("/update") + public Result update(@RequestBody @Validated(ValidationGroup.edit.class) MchStoreInfoParam param) { + ValidationUtil.validateParam(param, ValidationGroup.edit.class); + mchStoreInfoService.update(param); + return Res.ok(); + } + + @PermCode(code = "view", nameCn = "门店查看", nameEn = "Store View") + @Operation(summary = "门店分页") + @GetMapping("/page") + public Result> page(PageParam pageParam, MchStoreInfoQuery query) { + return Res.ok(mchStoreInfoService.page(pageParam, query)); + } + + @PermCode(code = "view", nameCn = "门店查看", nameEn = "Store View") + @Operation(summary = "根据id查询门店") + @GetMapping("/get") + public Result findById(@NotNull(message = "{validation.field.id.notNull}") Long id) { + return Res.ok(mchStoreInfoService.findById(id)); + } + + @PermCode(code = "delete", nameCn = "门店删除", nameEn = "Store Delete") + @Operation(summary = "删除门店") + @PostMapping("/delete") + public Result delete(@NotNull(message = "{validation.field.id.notNull}") Long id) { + mchStoreInfoService.delete(id); + return Res.ok(); + } +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/controller/store/MchStoreInfoController.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/controller/store/MchStoreInfoController.java new file mode 100644 index 000000000..148f83849 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/controller/store/MchStoreInfoController.java @@ -0,0 +1,69 @@ +package cn.daxpay.open.payment.merchant.controller.store; + +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoParam; +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoQuery; +import cn.daxpay.open.payment.merchant.result.store.MchStoreInfoResult; +import cn.daxpay.open.payment.merchant.service.store.MchStoreInfoService; +import cn.daxpay.open.platform.core.rest.Res; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.core.rest.result.PageResult; +import cn.daxpay.open.platform.core.rest.result.Result; +import cn.daxpay.open.platform.core.validation.ValidationGroup; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/// # 门店信息管理 +/// +@Validated +@Tag(name = "门店信息管理") +@RestController +@RequestMapping("/mch/store") +@RequiredArgsConstructor +public class MchStoreInfoController { + private final MchStoreInfoService mchStoreInfoService; + + @Operation(summary = "新增门店") + @PostMapping("/add") + public Result add(@RequestBody @Validated(ValidationGroup.add.class) MchStoreInfoParam param) { + mchStoreInfoService.add(param); + return Res.ok(); + } + + @Operation(summary = "修改门店") + @PostMapping("/update") + public Result update(@RequestBody @Validated(ValidationGroup.edit.class) MchStoreInfoParam param) { + mchStoreInfoService.update(param); + return Res.ok(); + } + + @Operation(summary = "门店分页") + @GetMapping("/page") + public Result> page(PageParam pageParam, MchStoreInfoQuery query) { + return Res.ok(mchStoreInfoService.page(pageParam, query)); + } + + @Operation(summary = "门店列表") + @GetMapping("/list") + public Result> list() { + return Res.ok(mchStoreInfoService.list()); + } + + @Operation(summary = "根据id查询门店") + @GetMapping("/get") + public Result findById(@NotNull(message = "{validation.field.id.notNull}") Long id) { + return Res.ok(mchStoreInfoService.findById(id)); + } + + @Operation(summary = "删除门店") + @PostMapping("/delete") + public Result delete(@NotNull(message = "{validation.field.id.notNull}") Long id) { + mchStoreInfoService.delete(id); + return Res.ok(); + } +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/convert/store/MchStoreInfoConvert.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/convert/store/MchStoreInfoConvert.java new file mode 100644 index 000000000..3ce67df3c --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/convert/store/MchStoreInfoConvert.java @@ -0,0 +1,24 @@ +package cn.daxpay.open.payment.merchant.convert.store; + +import cn.daxpay.open.payment.merchant.entity.store.MchStoreInfo; +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoParam; +import cn.daxpay.open.payment.merchant.result.store.MchStoreInfoResult; +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +/// # 门店信息转换 +/// +@Mapper +public interface MchStoreInfoConvert { + MchStoreInfoConvert CONVERT = Mappers.getMapper(MchStoreInfoConvert.class); + + MchStoreInfoResult toResult(MchStoreInfo entity); + + MchStoreInfo toEntity(MchStoreInfoParam param); + + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void copy(MchStoreInfoParam param, @MappingTarget MchStoreInfo mchStore); +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/dao/store/MchStoreInfoManager.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/dao/store/MchStoreInfoManager.java new file mode 100644 index 000000000..7567393f7 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/dao/store/MchStoreInfoManager.java @@ -0,0 +1,46 @@ +package cn.daxpay.open.payment.merchant.dao.store; + +import cn.daxpay.open.payment.merchant.entity.store.MchStoreInfo; +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoQuery; +import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager; +import cn.daxpay.open.platform.common.mybatisplus.query.generator.QueryGenerator; +import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/// # 门店信息管理 +/// +@Slf4j +@Repository +@RequiredArgsConstructor +public class MchStoreInfoManager extends BaseManager { + + /// 根据门店号查询 + public Optional findByStoreNo(String storeNo) { + return this.findByField(MchStoreInfo::getStoreNo, storeNo); + } + + /// 判断门店号是否存在 + public boolean existsByStoreNo(String storeNo) { + return existedByField(MchStoreInfo::getStoreNo, storeNo); + } + + /// 分页 + public Page page(PageParam pageParam, MchStoreInfoQuery query) { + Page mpPage = MpUtil.getMpPage(pageParam); + QueryWrapper wrapper = QueryGenerator.generator(query); + return this.page(mpPage, wrapper); + } + + /// 根据商户号查询所有门店 + public List findAllByMchNo(String mchNo) { + return this.findAllByField(MchStoreInfo::getMchNo, mchNo); + } +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/dao/store/MchStoreInfoMapper.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/dao/store/MchStoreInfoMapper.java new file mode 100644 index 000000000..6636913c7 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/dao/store/MchStoreInfoMapper.java @@ -0,0 +1,11 @@ +package cn.daxpay.open.payment.merchant.dao.store; + +import cn.daxpay.open.payment.merchant.entity.store.MchStoreInfo; +import com.github.yulichang.base.MPJBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/// # 门店信息 +/// +@Mapper +public interface MchStoreInfoMapper extends MPJBaseMapper { +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/entity/store/MchStoreInfo.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/entity/store/MchStoreInfo.java new file mode 100644 index 000000000..c86d1cf82 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/entity/store/MchStoreInfo.java @@ -0,0 +1,69 @@ +package cn.daxpay.open.payment.merchant.entity.store; + +import cn.daxpay.open.payment.common.entity.merchant.MchBaseEntity; +import cn.daxpay.open.payment.merchant.convert.store.MchStoreInfoConvert; +import cn.daxpay.open.payment.merchant.result.store.MchStoreInfoResult; +import cn.daxpay.open.platform.common.mybatisplus.function.ToResult; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import lombok.experimental.FieldNameConstants; + +import java.math.BigDecimal; + +/// # 门店信息 +/// +@EqualsAndHashCode(callSuper = true) +@Data +@FieldNameConstants +@Accessors(chain = true) +@TableName("mch_store_info") +public class MchStoreInfo extends MchBaseEntity implements ToResult { + + /// 门店号 + @TableField(updateStrategy = FieldStrategy.NEVER, fill = FieldFill.INSERT) + private String storeNo; + + /// 门店名称 + private String storeName; + + /// 联系人电话 + private String contactPhone; + + /// 门店LOGO + private String logoUrl; + + /// 门头照 + private String facadeUrl; + + /// 门店内景照 + private String interiorUrl; + + /// 行政区划代码 + private String regionCode; + + /// 详细地址 + private String address; + + /// 经度 + private BigDecimal longitude; + + /// 纬度 + private BigDecimal latitude; + + /// 状态 + /// @see cn.daxpay.open.platform.core.enums.merchant.StoreStatusEnum + private String status; + + /// 备注 + private String remark; + + @Override + public MchStoreInfoResult toResult() { + return MchStoreInfoConvert.CONVERT.toResult(this); + } +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/param/store/MchStoreInfoParam.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/param/store/MchStoreInfoParam.java new file mode 100644 index 000000000..b90353084 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/param/store/MchStoreInfoParam.java @@ -0,0 +1,74 @@ +package cn.daxpay.open.payment.merchant.param.store; + +import cn.daxpay.open.platform.core.validation.ValidationGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +/// # 门店信息 +/// +@Data +@Accessors(chain = true) +@Schema(title = "门店信息") +public class MchStoreInfoParam { + + /// 主键 + @Schema(description = "主键") + @NotNull(message = "{validation.field.id.notNull}", groups = ValidationGroup.edit.class) + private Long id; + + /// 商户号 + @Schema(description = "商户号") + private String mchNo; + + /// 门店名称 + @Schema(description = "门店名称") + @NotBlank(message = "{validation.field.storeName.notBlank}") + private String storeName; + + /// 联系人电话 + @Schema(description = "联系人电话") + private String contactPhone; + + /// 门店LOGO + @Schema(description = "门店LOGO") + private String logoUrl; + + /// 门头照 + @Schema(description = "门头照") + private String facadeUrl; + + /// 门店内景照 + @Schema(description = "门店内景照") + private String interiorUrl; + + /// 行政区划代码 + @Schema(description = "行政区划代码") + private String regionCode; + + /// 详细地址 + @Schema(description = "详细地址") + private String address; + + /// 经度 + @Schema(description = "经度") + private BigDecimal longitude; + + /// 纬度 + @Schema(description = "纬度") + private BigDecimal latitude; + + /// 状态 + /// @see cn.daxpay.open.platform.core.enums.merchant.StoreStatusEnum + @Schema(description = "状态") + @NotBlank(message = "{validation.field.status.notBlank}") + private String status; + + /// 备注 + @Schema(description = "备注") + private String remark; +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/param/store/MchStoreInfoQuery.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/param/store/MchStoreInfoQuery.java new file mode 100644 index 000000000..d0d5d3ce7 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/param/store/MchStoreInfoQuery.java @@ -0,0 +1,38 @@ +package cn.daxpay.open.payment.merchant.param.store; + +import cn.daxpay.open.platform.core.annotation.QueryParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 门店查询参数 +/// +@Data +@QueryParam(type = QueryParam.CompareTypeEnum.LIKE) +@Accessors(chain = true) +@Schema(title = "门店查询参数") +public class MchStoreInfoQuery { + + /// 商户号 + @Schema(description = "商户号") + @QueryParam(type = QueryParam.CompareTypeEnum.EQ) + private String mchNo; + + /// 门店号 + @Schema(description = "门店号") + @QueryParam(type = QueryParam.CompareTypeEnum.EQ) + private String storeNo; + + /// 门店名称 + @Schema(description = "门店名称") + private String storeName; + + /// 联系人电话 + @Schema(description = "联系人电话") + private String contactPhone; + + /// 状态 + @Schema(description = "状态") + @QueryParam(type = QueryParam.CompareTypeEnum.EQ) + private String status; +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/result/store/MchStoreInfoResult.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/result/store/MchStoreInfoResult.java new file mode 100644 index 000000000..54a422356 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/result/store/MchStoreInfoResult.java @@ -0,0 +1,59 @@ +package cn.daxpay.open.payment.merchant.result.store; + +import cn.daxpay.open.platform.core.result.BaseResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import lombok.experimental.FieldNameConstants; + +import java.math.BigDecimal; + +/// # 门店信息 +/// +@EqualsAndHashCode(callSuper = true) +@Data +@FieldNameConstants +@Accessors(chain = true) +@Schema(title = "门店信息") +public class MchStoreInfoResult extends BaseResult { + + @Schema(description = "商户号") + private String mchNo; + + @Schema(description = "门店号") + private String storeNo; + + @Schema(description = "门店名称") + private String storeName; + + @Schema(description = "联系人电话") + private String contactPhone; + + @Schema(description = "门店LOGO") + private String logoUrl; + + @Schema(description = "门头照") + private String facadeUrl; + + @Schema(description = "门店内景照") + private String interiorUrl; + + @Schema(description = "行政区划代码") + private String regionCode; + + @Schema(description = "详细地址") + private String address; + + @Schema(description = "经度") + private BigDecimal longitude; + + @Schema(description = "纬度") + private BigDecimal latitude; + + @Schema(description = "状态") + private String status; + + @Schema(description = "备注") + private String remark; +} diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/service/store/MchStoreInfoService.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/service/store/MchStoreInfoService.java new file mode 100644 index 000000000..0765f3a21 --- /dev/null +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/service/store/MchStoreInfoService.java @@ -0,0 +1,136 @@ +package cn.daxpay.open.payment.merchant.service.store; + +import cn.daxpay.open.payment.common.context.PaymentContext; +import cn.daxpay.open.payment.merchant.convert.store.MchStoreInfoConvert; +import cn.daxpay.open.payment.merchant.dao.info.MerchantInfoManager; +import cn.daxpay.open.payment.merchant.dao.store.MchStoreInfoManager; +import cn.daxpay.open.payment.merchant.entity.info.MerchantInfo; +import cn.daxpay.open.payment.merchant.entity.store.MchStoreInfo; +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoParam; +import cn.daxpay.open.payment.merchant.param.store.MchStoreInfoQuery; +import cn.daxpay.open.payment.merchant.result.store.MchStoreInfoResult; +import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil; +import cn.daxpay.open.platform.core.code.CommonCode; +import cn.daxpay.open.platform.core.enums.client.ClientEnum; +import cn.daxpay.open.platform.core.exception.BizException; +import cn.daxpay.open.platform.core.exception.BizInfoException; +import cn.daxpay.open.platform.core.exception.DataNotExistException; +import cn.daxpay.open.platform.core.exception.config.ConfigErrorException; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.core.rest.result.PageResult; +import cn.daxpay.open.platform.iam.service.client.ClientCodeService; +import cn.hutool.core.util.RandomUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +/// # 门店信息管理 +/// +@Slf4j +@Service +@RequiredArgsConstructor +public class MchStoreInfoService { + + private final MchStoreInfoManager mchStoreInfoManager; + + private final MerchantInfoManager merchantInfoManager; + + private final ClientCodeService clientCodeService; + + private final PaymentContext apiContext; + + /// 新增门店 + public void add(MchStoreInfoParam param) { + String mchNo = this.resolveMchNo(param.getMchNo()); + MerchantInfo merchant = merchantInfoManager.findByMchNo(mchNo) + // 商户: 商户不存在 + .orElseThrow(() -> new BizException(CommonCode.FAIL_CODE, "error.payment.merchant.mchNotExist")); + param.setMchNo(mchNo); + MchStoreInfo entity = MchStoreInfoConvert.CONVERT.toEntity(param); + // 生成门店号 + entity.setStoreNo(this.generateStoreNo()); + entity.setMchNo(mchNo); + mchStoreInfoManager.save(entity); + } + + /// 修改门店 + @Transactional(rollbackFor = Exception.class) + public void update(MchStoreInfoParam param) { + MchStoreInfo mchStore = mchStoreInfoManager.findById(param.getId()) + // 商户: 门店不存在 + .orElseThrow(() -> new DataNotExistException("error.payment.merchant.storeNotFound")); + this.checkStore(mchStore); + MchStoreInfoConvert.CONVERT.copy(param, mchStore); + mchStoreInfoManager.updateById(mchStore); + } + + /// 分页 + public PageResult page(PageParam pageParam, MchStoreInfoQuery query) { + return MpUtil.toPageResult(mchStoreInfoManager.page(pageParam, query)); + } + + /// 根据id查询 + public MchStoreInfoResult findById(Long id) { + MchStoreInfo mchStore = mchStoreInfoManager.findById(id) + // 商户: 门店不存在 + .orElseThrow(() -> new DataNotExistException("error.payment.merchant.storeNotFound")); + this.checkStore(mchStore); + return mchStore.toResult(); + } + + /// 门店列表(商户端按当前商户过滤) + public List list() { + String mchNo = this.resolveMchNo(null); + return mchStoreInfoManager.findAllByMchNo(mchNo).stream() + .map(MchStoreInfo::toResult) + .collect(Collectors.toList()); + } + + /// 删除 + public void delete(Long id) { + MchStoreInfo mchStore = mchStoreInfoManager.findById(id) + // 商户: 门店不存在 + .orElseThrow(() -> new DataNotExistException("error.payment.merchant.storeNotFound")); + this.checkStore(mchStore); + mchStoreInfoManager.deleteById(id); + } + + /// 生成门店号 + public String generateStoreNo() { + String storeNo = "S" + RandomUtil.randomNumbers(16); + for (int i = 0; i < 10; i++) { + if (!mchStoreInfoManager.existsByStoreNo(storeNo)) { + return storeNo; + } + storeNo = "S" + RandomUtil.randomNumbers(16); + } + // 商户: 门店号生成失败 + throw new BizException(CommonCode.FAIL_CODE, "error.payment.merchant.storeNoGenFailed"); + } + + /// 解析商户号, 商户端从上下文获取, 管理端从参数获取 + private String resolveMchNo(String paramMchNo) { + if (clientCodeService.getClientCode().equals(ClientEnum.MERCHANT.getCode())) { + return apiContext.getTradeInfo().getMchNo(); + } + if (paramMchNo == null) { + // 商户: 数据错误,未发现商户号 + throw new BizInfoException(CommonCode.FAIL_CODE, "error.payment.merchant.dataErrorNoMchNo"); + } + return paramMchNo; + } + + /// 如果和当前商户不匹配, 抛出错误(商户端校验) + public void checkStore(MchStoreInfo mchStore) { + if (clientCodeService.getClientCode().equals(ClientEnum.MERCHANT.getCode())) { + if (!mchStore.getMchNo().equals(apiContext.getTradeInfo().getMchNo())) { + // 商户: 门店不属于当前商户 + throw new ConfigErrorException("error.payment.merchant.storeNoMatch"); + } + } + } +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/store_status.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/store_status.json new file mode 100644 index 000000000..500de6ce4 --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/store_status.json @@ -0,0 +1,4 @@ +{ + "disabled": "Disabled", + "enable": "Enabled" +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/common.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/common.json index 49639790c..4988e5517 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/common.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/common.json @@ -27,5 +27,6 @@ "payRefundStatusNotExist": "Pay refund status not found: {0}", "payCallTypeNotSupported": "Unsupported checkout type: {0}", "channelAuthTypeNotExist": "Auth type not found: {0}", - "mchAppStatusNotFound": "Merchant app status not found: {0}" + "mchAppStatusNotFound": "Merchant app status not found: {0}", + "storeStatusNotFound": "Store status not found: {0}" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/payment/merchant.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/payment/merchant.json index 528a3cd6c..d7dcfee8d 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/payment/merchant.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/payment/merchant.json @@ -18,5 +18,8 @@ "specifiedAppConfigNotFound": "Specified application configuration not found", "specifiedMchConfigNotFound": "Specified merchant configuration not found", "adminRoleNotExist": "Merchant admin role not found, please check", - "apiConfigNotExist": "Merchant API configuration not found" + "apiConfigNotExist": "Merchant API configuration not found", + "storeNotFound": "Store not found", + "storeNoGenFailed": "Store number generation failed", + "storeNoMatch": "Store does not belong to the current merchant" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/store_status.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/store_status.json new file mode 100644 index 000000000..f2e6c2c57 --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/store_status.json @@ -0,0 +1,4 @@ +{ + "disabled": "停用", + "enable": "启用" +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/common.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/common.json index c0dc87c0b..13344df8a 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/common.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/common.json @@ -27,5 +27,6 @@ "payRefundStatusNotExist": "该退款状态不存在: {0}", "payCallTypeNotSupported": "不支持的收银台类型: {0}", "channelAuthTypeNotExist": "认证类型不存在: {0}", - "mchAppStatusNotFound": "未找到对应的商户应用状态类型: {0}" + "mchAppStatusNotFound": "未找到对应的商户应用状态类型: {0}", + "storeStatusNotFound": "未找到对应的门店状态类型: {0}" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/payment/merchant.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/payment/merchant.json index 4af508fe7..fb140da03 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/payment/merchant.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/payment/merchant.json @@ -18,5 +18,8 @@ "specifiedAppConfigNotFound": "未找到指定的应用配置", "specifiedMchConfigNotFound": "未找到指定的商户配置", "adminRoleNotExist": "商户管理员角色不存在, 请检查", - "apiConfigNotExist": "商户API配置不存在" + "apiConfigNotExist": "商户API配置不存在", + "storeNotFound": "门店信息不存在", + "storeNoGenFailed": "门店号生成失败", + "storeNoMatch": "门店不属于当前商户" } diff --git a/daxpay-platform/daxpay-platform-core/src/main/java/cn/daxpay/open/platform/core/enums/merchant/StoreStatusEnum.java b/daxpay-platform/daxpay-platform-core/src/main/java/cn/daxpay/open/platform/core/enums/merchant/StoreStatusEnum.java new file mode 100644 index 000000000..4c4af75c8 --- /dev/null +++ b/daxpay-platform/daxpay-platform-core/src/main/java/cn/daxpay/open/platform/core/enums/merchant/StoreStatusEnum.java @@ -0,0 +1,40 @@ +package cn.daxpay.open.platform.core.enums.merchant; + +import cn.daxpay.open.platform.core.exception.config.ConfigNotExistException; +import cn.daxpay.open.platform.core.i18n.I18nSupport; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/// # 门店状态 +/// +/// 字典: store_status +@Getter +@RequiredArgsConstructor +public enum StoreStatusEnum implements I18nSupport { + + /// 禁用 + DISABLED("disabled"), + /// 启用 + ENABLE("enable"); + + /// 编码 + private final String code; + + /// 翻译 key 前缀 + @Override + public String getI18nPrefix() { + return "enum.store_status"; + } + + /// 根据编码查找 + public static StoreStatusEnum findByCode(String code) { + return Arrays.stream(values()) + .filter(e -> e.getCode().equals(code)) + .findFirst() + // 通用: 未找到对应的门店状态类型: {0} + .orElseThrow(() -> new ConfigNotExistException("error.common.storeStatusNotFound", code)); + } + +}