diff --git a/pom.xml b/pom.xml
index ab1ae6b..28f0542 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,25 +17,7 @@
25
-
-
-
-
-
- org.springframework.cloud
- spring-cloud-dependencies
- 2025.0.1
- pom
- import
-
-
-
-
-
- org.springframework.cloud
- spring-cloud-starter-openfeign
-
org.springframework.boot
spring-boot-starter-web
@@ -94,22 +76,6 @@
commons-lang3
3.18.0
-
-
- com.tencentcloudapi
- tencentcloud-sdk-java
- 3.1.1396
-
-
- com.qcloud
- cos_api
- 5.6.246
-
-
- org.apache.tika
- tika-core
- 3.2.3
-
diff --git a/src/main/java/com/xjhs/findmemerchant/common/jpa/AbstractBaseEntity.java b/src/main/java/com/xjhs/findmemerchant/common/jpa/AbstractBaseEntity.java
index d91a194..0d16da2 100644
--- a/src/main/java/com/xjhs/findmemerchant/common/jpa/AbstractBaseEntity.java
+++ b/src/main/java/com/xjhs/findmemerchant/common/jpa/AbstractBaseEntity.java
@@ -34,7 +34,7 @@ public class AbstractBaseEntity {
@CreatedDate
@Column(name = "created_at", nullable = false, updatable = false)
@Comment("创建时间")
- private LocalDateTime createTime;
+ private LocalDateTime createdAt;
/**
* 创建人
*/
@@ -49,7 +49,7 @@ public class AbstractBaseEntity {
@LastModifiedDate
@Column(name = "updated_at", nullable = false)
@Comment("更新时间")
- private LocalDateTime updateTime;
+ private LocalDateTime updatedAt;
/**
* 更新人
*/
@@ -62,20 +62,20 @@ public class AbstractBaseEntity {
*/
@Column(name = "deleted_at")
@Comment("软删除时间")
- private LocalDateTime deleteTime;
+ private LocalDateTime deletedAt;
/**
* 是否已删除
*/
public boolean isDeleted() {
- return deleteTime != null;
+ return deletedAt != null;
}
/**
* 标记删除
*/
public void markDeleted() {
- this.deleteTime = LocalDateTime.now();
+ this.deletedAt = LocalDateTime.now();
}
}
diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCOSService.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCOSService.java
deleted file mode 100644
index 63ae89c..0000000
--- a/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCOSService.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.xjhs.findmemerchant.common.openapi;
-
-import com.qcloud.cos.COSClient;
-import com.qcloud.cos.exception.CosClientException;
-import com.qcloud.cos.model.GetObjectRequest;
-import com.qcloud.cos.model.PutObjectRequest;
-import com.xjhs.findmemerchant.common.openapi.dto.CosPutObjectResult;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.UUID;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class TencentCOSService {
- @Value("${appconfig.tencentCos.bucketName}")
- private String bucketName;
- private final COSClient tencentCosClient;
-
- /**
- * 文件上传到Cos存储桶
- *
- * @param file 文件
- * @return 存储对象信息
- */
- public CosPutObjectResult putObject(File file) throws Exception {
- try {
- var key = UUID.randomUUID().toString();
- var req = new PutObjectRequest(this.bucketName, key, file);
- var result = tencentCosClient.putObject(req);
- return new CosPutObjectResult(key, result.getETag());
- } catch (CosClientException e) {
- log.error("文件上传到对象存储失败", e);
- throw new Exception("文件上传到对象存储失败");
- }
- }
-
- /**
- * 获取文件输入流(下载文件)
- * @param key 对象key
- * @return 输入流
- * @throws Exception 下载失败
- */
- public InputStream getObject(String key) throws Exception{
- try {
- var req = new GetObjectRequest(this.bucketName, key);
- var cosObject = this.tencentCosClient.getObject(req);
- return cosObject.getObjectContent();
- } catch (CosClientException e) {
- log.error("从对象存储下载文件失败",e);
- throw new Exception("从对象存储下载文件失败");
- }
- }
-
-
- /**
- * 删除存储对象
- *
- * @param key 对象key
- * @throws Exception 删除失败
- */
- public void deleteObject(String key) throws Exception {
- try {
- this.tencentCosClient.deleteObject(this.bucketName, key);
- } catch (CosClientException e) {
- log.error("文件上传到对象存储失败", e);
- throw new Exception("文件上传到对象存储失败");
- }
- }
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCloudSMSService.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCloudSMSService.java
deleted file mode 100644
index 8c4c583..0000000
--- a/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCloudSMSService.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.xjhs.findmemerchant.common.openapi;
-
-
-import com.tencentcloudapi.common.exception.TencentCloudSDKException;
-import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
-import com.tencentcloudapi.sms.v20190711.SmsClient;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor()
-public class TencentCloudSMSService {
- @Value("${appconfig.tencentSms.sdkAppId}")
- private String sdkAppid;
- @Value("${appconfig.tencentSms.templateId}")
- private String templateId;
- @Value("${appconfig.tencentSms.signName}")
- private String signName;
- private final SmsClient tencentSmsClient;
-
-
- /**
- * 发送短信验证码
- *
- * @param phone 11位国内手机号码
- * @param code 验证码
- * @return 验证码值
- * @throws Exception 发送失败
- */
- public String sendVerifyCode(String phone, String code) throws Exception {
- try {
- if (!phone.startsWith("+86")) {
- phone = "+86" + phone;
- }
- var req = new SendSmsRequest();
- req.setSmsSdkAppid(this.sdkAppid);
- req.setTemplateID(this.templateId);
- req.setSign(this.signName);
- req.setPhoneNumberSet(new String[]{phone});
- req.setTemplateParamSet(new String[]{code});
- var resp = this.tencentSmsClient.SendSms(req);
- if (resp.getSendStatusSet().length == 0){
- throw new Exception("取回短信发送结果失败");
- }
- if (!"ok".equalsIgnoreCase(resp.getSendStatusSet()[0].getCode())){
- throw new Exception(resp.getSendStatusSet()[0].getMessage());
- }
- return code;
- } catch (TencentCloudSDKException e) {
- log.error("验证码发送失败", e);
- throw new Exception("系统错误,验证码发送失败");
- }
- }
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapFeignClient.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapFeignClient.java
deleted file mode 100644
index a98d2bf..0000000
--- a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapFeignClient.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.xjhs.findmemerchant.common.openapi.amap;
-
-
-import com.xjhs.findmemerchant.common.openapi.amap.response.AmapGeocodeResponse;
-import com.xjhs.findmemerchant.common.openapi.amap.response.AmapReGeocodeResponse;
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-
-/**
- * 高德地图服务接口
- */
-@FeignClient(name = "amapFeignClient", url = "https://restapi.amap.com")
-public interface AmapFeignClient {
-
- /**
- * 地理编码 API 服务
- * @param key 高德Key
- * @param address 结构化地址信息,规则遵循:国家、省份、城市、区县、城镇、乡村、街道、门牌号码、屋邨、大厦,如:北京市朝阳区阜通东大街6号。
- * @return 响应信息
- */
- @GetMapping("/v3/geocode/geo")
- AmapGeocodeResponse getGeo(@RequestParam(name = "key") String key,
- @RequestParam(name = "address") String address);
-
-
- /**
- * 逆地理编码 API 服务地址
- * @param key 高德Key
- * @param location 经纬度坐标 传入内容规则:经度在前,纬度在后,经纬度间以“,”分割,经纬度小数点后不要超过 6 位。
- */
- @GetMapping("/v3/geocode/regeo")
- AmapReGeocodeResponse getReGeo(@RequestParam(name = "key") String key,
- @RequestParam(name = "location") String location);
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapService.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapService.java
deleted file mode 100644
index a9583fb..0000000
--- a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapService.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.xjhs.findmemerchant.common.openapi.amap;
-
-import com.xjhs.findmemerchant.common.openapi.amap.response.AmapGeocodeResponse;
-import com.xjhs.findmemerchant.common.openapi.amap.response.AmapReGeocodeResponse;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class AmapService {
-
- @Value("${appconfig.amapKey}")
- private String amapKey = "c618de6e686c43095a8593db836c7de2";
- private final AmapFeignClient amapFeignClient;
-
- /**
- * 地址转经纬度
- *
- * @param address 地址信息
- * @return 经纬度信息结果
- */
- public AmapGeocodeResponse getGeo(String address) {
- return this.amapFeignClient.getGeo(this.amapKey, address);
- }
-
- /**
- * 经纬度转地理位置信息
- *
- * @param lng 经度
- * @param lat 纬度
- * @return 位置信息
- */
- public AmapReGeocodeResponse getRegeo(double lng, double lat) {
- return this.amapFeignClient.getReGeo(this.amapKey, lng + "," + lat);
- }
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapGeocodeResponse.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapGeocodeResponse.java
deleted file mode 100644
index 0969541..0000000
--- a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapGeocodeResponse.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.xjhs.findmemerchant.common.openapi.amap.response;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 地理编码 API 服务响应内容
- */
-@Data
-public class AmapGeocodeResponse {
- /**
- * 请求状态值,0 表示请求失败,1 表示请求成功
- */
- private int status;
-
- /**
- * 返回结果的数量
- */
- private int count;
-
- /**
- * 返回结果的详细说明,当 status 为 0 时,info 会包含错误原因;否则返回 "OK"
- */
- private String info;
-
- /**
- * 地理编码信息列表,包含多个地址组件
- */
- private List geocodes = new ArrayList<>();
-
- /**
- * 地理编码信息的详细字段
- */
- @Data
- public static class GeocodeDetails {
-
- @JsonProperty("formatted_address")
- private String address;
-
- /**
- * 国家,默认返回中国
- */
- private String country;
-
- /**
- * 省份名称,例如:北京市
- */
- private String province;
-
- /**
- * 城市名称,例如:北京市
- */
- private String city;
-
- /**
- * 城市编码,例如:010
- */
- private String citycode;
-
- /**
- * 区域名称,例如:朝阳区
- */
- private String district;
-
- /**
- * 街道名称,例如:单通东大街
- */
- private String street;
-
- /**
- * 门牌号,例如:6号
- */
- private String number;
-
- /**
- * 区域编码,例如:110101
- */
- private String adcode;
-
- /**
- * 经纬度坐标,逗号分隔
- */
- private String location;
-
- /**
- * 匹配级别,用于地理匹配的精确度
- */
- private String level;
- }
-
-
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapReGeocodeResponse.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapReGeocodeResponse.java
deleted file mode 100644
index 11d01dd..0000000
--- a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapReGeocodeResponse.java
+++ /dev/null
@@ -1,207 +0,0 @@
-package com.xjhs.findmemerchant.common.openapi.amap.response;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 逆地理位置服务响应内容
- */
-@Data
-public class AmapReGeocodeResponse {
-
- /**
- * 请求状态,1 表示请求成功
- */
- private String status;
-
- /**
- * 地址组件信息
- */
- private Regeocode regeocode;
-
- /**
- * 返回的状态信息
- */
- private String info;
-
- /**
- * 状态码,10000 表示成功
- */
- private String infocode;
-
- /**
- * 地址组件类,包含具体的地理信息
- */
- @Data
- public static class Regeocode {
-
- /**
- * 地址组件,包含城市、区、街道等信息
- */
- private AddressComponent addressComponent;
-
- /**
- * 格式化后的地址,整合了详细信息
- */
- @JsonProperty("formatted_address")
- private String address;
- }
-
- /**
- * 地址组件,包含详细的地理信息
- */
- @Data
- public static class AddressComponent {
-
- /**
- * 城市名称,可能为空
- */
- private List city = new ArrayList<>();
-
- /**
- * 省份名称
- */
- private String province;
-
- /**
- * 区域编码
- */
- private String adcode;
-
- /**
- * 区域名称
- */
- private String district;
-
- /**
- * 城镇编码
- */
- private String towncode;
-
- /**
- * 街道信息
- */
- private StreetNumber streetNumber;
-
- /**
- * 国家名称
- */
- private String country;
-
- /**
- * 街道所属乡镇名称
- */
- private String township;
-
- /**
- * 商圈信息,包含多个商圈
- */
- private List businessAreas;
-
- /**
- * 建筑信息
- */
- private Building building;
-
- /**
- * 邻里信息
- */
- private Neighborhood neighborhood;
-
- /**
- * 城市编码
- */
- private String citycode;
- }
-
- /**
- * 街道信息,包括街道名称、位置、方向等
- */
- @Data
- public static class StreetNumber {
-
- /**
- * 门牌号
- */
- private String number;
-
- /**
- * 经纬度位置
- */
- private String location;
-
- /**
- * 方向
- */
- private String direction;
-
- /**
- * 距离
- */
- private String distance;
-
- /**
- * 街道名称
- */
- private String street;
- }
-
- /**
- * 商圈信息
- */
- @Data
- public static class BusinessArea {
-
- /**
- * 商圈位置(经纬度)
- */
- private String location;
-
- /**
- * 商圈名称
- */
- private String name;
-
- /**
- * 商圈ID
- */
- private String id;
- }
-
- /**
- * 建筑信息,包括建筑名称和类型
- */
- @Data
- public static class Building {
-
- /**
- * 建筑名称
- */
- private String name;
-
- /**
- * 建筑类型
- */
- private String type;
- }
-
- /**
- * 邻里信息
- */
- @Data
- public static class Neighborhood {
-
- /**
- * 邻里名称
- */
- private String name;
-
- /**
- * 邻里类型
- */
- private String type;
- }
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/dto/CosPutObjectResult.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/dto/CosPutObjectResult.java
deleted file mode 100644
index 4ff570a..0000000
--- a/src/main/java/com/xjhs/findmemerchant/common/openapi/dto/CosPutObjectResult.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.xjhs.findmemerchant.common.openapi.dto;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-public class CosPutObjectResult {
- private String key;
- private String eTag;
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/config/FeignConfig.java b/src/main/java/com/xjhs/findmemerchant/config/FeignConfig.java
deleted file mode 100644
index d3790a1..0000000
--- a/src/main/java/com/xjhs/findmemerchant/config/FeignConfig.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.xjhs.findmemerchant.config;
-
-import com.xjhs.findmemerchant.common.openapi.amap.AmapFeignClient;
-import org.springframework.cloud.openfeign.EnableFeignClients;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@EnableFeignClients(basePackageClasses = {
- AmapFeignClient.class,
-})
-public class FeignConfig {
-
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/config/JpaConfig.java b/src/main/java/com/xjhs/findmemerchant/config/JpaConfig.java
index a64a9ec..18bf7fa 100644
--- a/src/main/java/com/xjhs/findmemerchant/config/JpaConfig.java
+++ b/src/main/java/com/xjhs/findmemerchant/config/JpaConfig.java
@@ -1,6 +1,6 @@
package com.xjhs.findmemerchant.config;
-import com.xjhs.findmemerchant.security.LoginUser;
+import com.xjhs.findmemerchant.entity.Merchant;
import com.xjhs.findmemerchant.security.sms.SmsAuthenticationToken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -20,8 +20,8 @@ public class JpaConfig {
var auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof SmsAuthenticationToken){
var principal = auth.getPrincipal();
- if (principal instanceof LoginUser loginUser){
- return Optional.of(loginUser.getUserId());
+ if (principal instanceof Merchant merchant){
+ return Optional.of(merchant.getId());
}
}
return Optional.of(0L);
diff --git a/src/main/java/com/xjhs/findmemerchant/config/ThirdOpenApiConfig.java b/src/main/java/com/xjhs/findmemerchant/config/ThirdOpenApiConfig.java
deleted file mode 100644
index 6c66601..0000000
--- a/src/main/java/com/xjhs/findmemerchant/config/ThirdOpenApiConfig.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.xjhs.findmemerchant.config;
-
-import com.qcloud.cos.COSClient;
-import com.qcloud.cos.ClientConfig;
-import com.qcloud.cos.auth.BasicCOSCredentials;
-import com.tencentcloudapi.common.Credential;
-import com.tencentcloudapi.common.profile.ClientProfile;
-import com.tencentcloudapi.common.profile.HttpProfile;
-import com.tencentcloudapi.common.profile.Region;
-import com.tencentcloudapi.sms.v20190711.SmsClient;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class ThirdOpenApiConfig {
-
- @Bean
- public SmsClient tencentSmsClient(@Value("${appconfig.tencentSms.secretId}") String secretId,
- @Value("${appconfig.tencentSms.secretKey}") String secretKey) {
- var cred = new Credential(secretId, secretKey);
- var httpProfile = new HttpProfile();
- httpProfile.setEndpoint("sms.tencentcloudapi.com");
- var clientProfile = new ClientProfile();
- clientProfile.setHttpProfile(httpProfile);
- return new SmsClient(cred, "ap-guangzhou", clientProfile);
- }
-
- @Bean
- public COSClient tencentCosClient(@Value("${appconfig.tencentCos.secretId}") String secretId,
- @Value("${appconfig.tencentCos.secretKey}") String secretKey){
- var cred = new BasicCOSCredentials(secretId,secretKey);
- ClientConfig clientConfig = new ClientConfig(new com.qcloud.cos.region.Region("ap-guangzhou"));
- return new COSClient(cred, clientConfig);
- }
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/controller/AuthController.java b/src/main/java/com/xjhs/findmemerchant/controller/AuthController.java
index c854ced..745053d 100644
--- a/src/main/java/com/xjhs/findmemerchant/controller/AuthController.java
+++ b/src/main/java/com/xjhs/findmemerchant/controller/AuthController.java
@@ -1,22 +1,20 @@
package com.xjhs.findmemerchant.controller;
import com.xjhs.findmemerchant.common.ApiResult;
+import com.xjhs.findmemerchant.dto.MerchantDto;
import com.xjhs.findmemerchant.dto.auth.RegisterDto;
-import com.xjhs.findmemerchant.dto.merchant.MerchantDto;
import com.xjhs.findmemerchant.entity.Merchant;
-import com.xjhs.findmemerchant.mapper.MerchantMapper;
import com.xjhs.findmemerchant.redis.TokenBlacklistRedisService;
import com.xjhs.findmemerchant.repository.MerchantRepository;
import com.xjhs.findmemerchant.security.JwtTokenService;
-import com.xjhs.findmemerchant.security.LoginUser;
import com.xjhs.findmemerchant.security.RefreshTokenService;
import com.xjhs.findmemerchant.security.sms.SmsAuthenticationToken;
import com.xjhs.findmemerchant.security.sms.SmsCodeService;
-import com.xjhs.findmemerchant.system.SystemUserService;
import com.xjhs.findmemerchant.vo.auth.SmsLoginVo;
import com.xjhs.findmemerchant.vo.auth.SmsSendVo;
-import com.xjhs.findmemerchant.vo.auth.RegisterVo;
+import com.xjhs.findmemerchant.service.MerchantService;
import com.xjhs.findmemerchant.vo.merchant.MerchantUpdateVo;
+import com.xjhs.findmemerchant.vo.auth.RegisterVo;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -24,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@@ -43,8 +40,7 @@ public class AuthController {
private final RefreshTokenService refreshTokenService;
private final MerchantRepository merchantRepository;
private final TokenBlacklistRedisService tokenBlacklistRedisService;
- private final SystemUserService systemUserService;
- private final MerchantMapper merchantMapper;
+ private final MerchantService merchantService;
/**
* 发送短信验证码
@@ -126,46 +122,41 @@ public class AuthController {
* @return 注册成功返回登录令牌信息
*/
@PostMapping("/register")
- @Transactional
- public ApiResult register(@Valid @RequestBody RegisterVo registerVo) throws Exception {
+ public ApiResult register(@Valid @RequestBody RegisterVo registerVo) {
try {
this.smsCodeService.verifyCode(registerVo.getPhone(), "register", registerVo.getCode());
- var systemUser = this.systemUserService.getAndRegisterByPhone(registerVo.getPhone());
- if (systemUser.getMerchant() != null){
- throw new Exception("手机号已被注册");
+ var exists = merchantRepository.existsByPhone(registerVo.getPhone());
+ if (exists) {
+ return ApiResult.fail("手机号已被注册");
}
-
var merchant = new Merchant();
- merchant.setSystemUser(systemUser);
- systemUser.setMerchant(merchant);
+ merchant.setPhone(registerVo.getPhone());
this.merchantRepository.save(merchant);
return ApiResult.data(
- new RegisterDto(
- merchant.getId().toString(),
- this.jwtTokenService.generateToken(registerVo.getPhone()),
- this.refreshTokenService.create(registerVo.getPhone())
- )
+ new RegisterDto(
+ merchant.getId(),
+ this.jwtTokenService.generateToken(registerVo.getPhone()),
+ this.refreshTokenService.create(registerVo.getPhone())
+ )
);
} catch (Exception e) {
log.error("注册失败", e);
- throw e;
+ return ApiResult.fail("注册失败:" + e.getMessage());
}
}
/**
* 获取当前登录的商家基本信息
*
- * @param loginUser @ignore 当前登录用户
+ * @param merchant @ignore 当前商家
* @return 商家信息
*/
@GetMapping("/profile")
- @Transactional(readOnly = true)
- public ApiResult getProfile(@AuthenticationPrincipal LoginUser loginUser) {
- if (loginUser.getMerchantId() == null) {
+ public ApiResult getProfile(@AuthenticationPrincipal Merchant merchant) {
+ if (merchant == null) {
return ApiResult.fail("商家信息不存在");
}
- return this.merchantRepository.findById(loginUser.getMerchantId())
- .map(this.merchantMapper::toDto)
+ return this.merchantService.getById(merchant.getId())
.map(ApiResult::data)
.orElse(ApiResult.fail("商家信息不存在"));
}
@@ -181,8 +172,8 @@ public class AuthController {
public ApiResult updateProfile(@AuthenticationPrincipal Merchant merchant,
@Valid @RequestBody MerchantUpdateVo merchantUpdateVo) {
try {
- // TODO : 待完成
- return ApiResult.data(null);
+ var result = this.merchantService.updateMerchant(merchant.getId(), merchantUpdateVo);
+ return ApiResult.data(result);
} catch (Exception e) {
return ApiResult.fail(e.getMessage());
}
diff --git a/src/main/java/com/xjhs/findmemerchant/controller/MerchantController.java b/src/main/java/com/xjhs/findmemerchant/controller/MerchantController.java
new file mode 100644
index 0000000..332bc93
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/controller/MerchantController.java
@@ -0,0 +1,75 @@
+package com.xjhs.findmemerchant.controller;
+
+import com.xjhs.findmemerchant.common.ApiResult;
+import com.xjhs.findmemerchant.dto.MerchantDto;
+import com.xjhs.findmemerchant.entity.Merchant;
+import com.xjhs.findmemerchant.service.MerchantService;
+import com.xjhs.findmemerchant.vo.merchant.MerchantUpdateVo;
+import com.xjhs.findmemerchant.vo.merchant.MerchantVerifyVo;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.*;
+
+import java.security.Principal;
+
+/**
+ * 商家管理接口
+ */
+@Slf4j
+@RestController
+@RequestMapping("/merchant")
+@RequiredArgsConstructor
+public class MerchantController {
+ private final MerchantService merchantService;
+
+ /**
+ * 查询当前登录商家详情
+ *
+ * @param merchant @ignore 当前商家
+ * @return 商家详情信息
+ */
+ @GetMapping
+ public ApiResult getMerchant(@AuthenticationPrincipal Merchant merchant) {
+ return this.merchantService.getById(merchant.getId())
+ .map(ApiResult::data)
+ .orElse(ApiResult.fail("商家信息不存在"));
+ }
+
+ /**
+ * 更新当前登录商家信息
+ *
+ * @param merchant @ignore 当前商家
+ * @param merchantUpdateVo 更新参数
+ * @return 商家信息
+ */
+ @PutMapping
+ public ApiResult updateMerchant(@AuthenticationPrincipal Merchant merchant,
+ @Valid @RequestBody MerchantUpdateVo merchantUpdateVo) {
+ try {
+ var dto = this.merchantService.updateMerchant(merchant.getId(), merchantUpdateVo);
+ return ApiResult.data(dto);
+ } catch (Exception e) {
+ return ApiResult.fail(e.getMessage());
+ }
+ }
+
+ /**
+ * 当前商家验证
+ *
+ * @param merchant @ignore 当前商家
+ * @param merchantVerifyVo 验证参数
+ * @return 商家信息
+ */
+ @PostMapping("/verify")
+ public ApiResult verifyMerchant(@AuthenticationPrincipal Merchant merchant,
+ @Valid @RequestBody MerchantVerifyVo merchantVerifyVo) {
+ try {
+ var dto = this.merchantService.verifyMerchant(merchant.getId(), merchantVerifyVo.getIdCardNo(), merchantVerifyVo.getRealName());
+ return ApiResult.data(dto);
+ } catch (Exception e) {
+ return ApiResult.fail(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/controller/StoreController.java b/src/main/java/com/xjhs/findmemerchant/controller/StoreController.java
new file mode 100644
index 0000000..e2bbe03
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/controller/StoreController.java
@@ -0,0 +1,237 @@
+package com.xjhs.findmemerchant.controller;
+
+import com.xjhs.findmemerchant.common.ApiResult;
+import com.xjhs.findmemerchant.common.PageData;
+import com.xjhs.findmemerchant.common.jpa.query.JpaSpecs;
+import com.xjhs.findmemerchant.common.mvc.PageVo;
+import com.xjhs.findmemerchant.dto.store.BusinessPeriodDto;
+import com.xjhs.findmemerchant.dto.store.StoreBusinessStatusDto;
+import com.xjhs.findmemerchant.dto.store.StoreDto;
+import com.xjhs.findmemerchant.entity.BusinessPeriod;
+import com.xjhs.findmemerchant.entity.Merchant;
+import com.xjhs.findmemerchant.entity.Store;
+import com.xjhs.findmemerchant.mapper.StoreMapper;
+import com.xjhs.findmemerchant.repository.BusinessPeriodRepository;
+import com.xjhs.findmemerchant.repository.MerchantRepository;
+import com.xjhs.findmemerchant.repository.StoreRepository;
+import com.xjhs.findmemerchant.vo.store.BusinessPeriodVo;
+import com.xjhs.findmemerchant.vo.store.StoreBusinessStatusUpdateVo;
+import com.xjhs.findmemerchant.vo.store.StoreCreateVo;
+import com.xjhs.findmemerchant.vo.store.StoreUpdateVo;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.security.Principal;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 门店管理接口
+ */
+@Slf4j
+@RestController
+@RequestMapping("/stores")
+@RequiredArgsConstructor
+public class StoreController {
+ private final MerchantRepository merchantRepository;
+ private final StoreRepository storeRepository;
+ private final StoreMapper storeMapper;
+ private final BusinessPeriodRepository businessPeriodRepository;
+
+
+ /**
+ * 门店信息(分页查询)
+ *
+ * @param pageVo 分页参数
+ * @param merchant @ignore 当前登录商家
+ * @return 分页数据信息
+ */
+ @GetMapping
+ @Transactional(readOnly = true)
+ public ApiResult> findPage(@AuthenticationPrincipal Merchant merchant, PageVo pageVo) {
+ var pageData = this.storeRepository.findAll(Specification.allOf(
+ JpaSpecs.eq("merchant.id", merchant.getId())
+ ), pageVo.getPageable()).map(this.storeMapper::toDto);
+ return ApiResult.page(pageData.getTotalElements(), pageData.getContent());
+ }
+
+ /**
+ * 创建商家的门店
+ *
+ * @param vo 门店信息
+ * @param merchant @ignore 当前登录的商家
+ * @return 门店信息
+ */
+ @PostMapping
+ @Transactional(rollbackFor = Exception.class)
+ public ApiResult create(@AuthenticationPrincipal Merchant merchant,
+ @Valid @RequestBody StoreCreateVo vo) {
+ var store = this.storeMapper.toEntity(vo);
+ merchant.addStore(store);
+ this.storeRepository.save(store);
+ var dto = this.storeMapper.toDto(store);
+ return ApiResult.data(dto);
+ }
+
+ /**
+ * 查询门店详情
+ *
+ * @param storeId 门店id
+ * @param merchant @ignore 当前登录的商家
+ * @return 门店详情
+ */
+ @GetMapping("/{storeId}")
+ @Transactional(readOnly = true)
+ public ApiResult findById(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") String storeId) {
+ var storeIdLong = Long.parseLong(storeId);
+ return merchant.getStores().stream()
+ .filter(x -> Objects.equals(x.getId(), storeIdLong))
+ .map(this.storeMapper::toDto)
+ .map(ApiResult::data)
+ .findFirst()
+ .orElse(ApiResult.fail("门店信息不存在"));
+ }
+
+ /**
+ * 更新门店信息
+ *
+ * @param storeId 门店id
+ * @param merchant @ignore 当前登录的商家
+ * @param vo 更新对象
+ * @return 门店信息
+ */
+ @PutMapping("/{storeId}")
+ @Transactional(rollbackFor = Exception.class)
+ public ApiResult updateById(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") String storeId,
+ @Valid @RequestBody StoreUpdateVo vo) {
+ var storeIdLong = Long.parseLong(storeId);
+ for (Store store : merchant.getStores()) {
+ if (Objects.equals(storeIdLong, store.getId())) {
+ this.storeMapper.updateFromVo(vo, store);
+ this.storeRepository.save(store);
+ return ApiResult.data(this.storeMapper.toDto(store));
+ }
+ }
+ return ApiResult.fail("门店信息不存在");
+ }
+
+ /**
+ * 删除门店
+ *
+ * @param storeId 门店id
+ * @param merchant @ignore 当前登录的商家
+ */
+ @DeleteMapping("/{storeId}")
+ @Transactional(rollbackFor = Exception.class)
+ public ApiResult delteById(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") String storeId) {
+ var storeIdLong = Long.parseLong(storeId);
+ merchant.getStores().removeIf(x -> Objects.equals(storeIdLong, x.getId()));
+ this.merchantRepository.save(merchant);
+ return ApiResult.success("删除成功");
+ }
+
+ /**
+ * 获取门店营业状态
+ *
+ * @param storeId 门店id
+ * @param merchant @ignore 当前商户
+ * @return 门店营业状态信息
+ */
+ @GetMapping("/{storeId}/business-status")
+ @Transactional(readOnly = true)
+ public ApiResult getStoreBusinessStatus(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") String storeId) {
+ var storeIdLong = Long.parseLong(storeId);
+ return merchant.getStores().stream()
+ .filter(x -> Objects.equals(x.getId(), storeIdLong))
+ .map(this.storeMapper::toBusinessStatusDto)
+ .map(ApiResult::data)
+ .findFirst()
+ .orElse(ApiResult.fail("门店信息不存在"));
+ }
+
+ /**
+ * 设置门店营业状态
+ *
+ * @param storeId 门店id
+ * @param merchant @ignore 当前商家
+ * @param updateVo 更新参数
+ * @return 门店营业状态信息
+ */
+ @PutMapping("/{storeId}/business-status")
+ @Transactional(rollbackFor = Exception.class)
+ public ApiResult putStoreBusinessStatus(@PathVariable("storeId") String storeId,
+ @AuthenticationPrincipal Merchant merchant,
+ @Validated @RequestBody StoreBusinessStatusUpdateVo updateVo) {
+ var storeIdLong = Long.parseLong(storeId);
+ return merchant.getStores().stream()
+ .filter(x -> Objects.equals(x.getId(), storeIdLong))
+ .findFirst()
+ .map(x -> {
+ this.storeMapper.updateFromBusinessUpdateVo(updateVo, x);
+ this.storeRepository.save(x);
+ return this.storeMapper.toBusinessStatusDto(x);
+ })
+ .map(ApiResult::data)
+ .orElse(ApiResult.fail("门店信息不存在"));
+
+ }
+
+ /**
+ * 获取门店营业时间段
+ *
+ * @param storeId 门店id
+ * @param merchant @ignore 当前商家
+ * @return 门店营业时间段 列表
+ */
+ @GetMapping("/{storeId}/business-periods")
+ public ApiResult> getStoreBusinessPeriodList(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") String storeId) {
+ var storeIdLong = Long.parseLong(storeId);
+ return merchant.getStores().stream()
+ .filter(x -> Objects.equals(x.getId(), storeIdLong))
+ .findFirst()
+ .map(x -> this.storeMapper.toDtoList(x.getBusinessPeriods()))
+ .map(ApiResult::data)
+ .orElse(ApiResult.fail("门店信息不存在"));
+ }
+
+ /**
+ * 设置门店营业时间段
+ *
+ * @param storeId 门店id
+ * @param merchant @ignore 当前商家
+ * @param updateVoList 门店营业时间段更新参数列表
+ * @return 门店营业时间段 列表
+ */
+ @PutMapping("/{storeId}/business-periods")
+ public ApiResult> putStoreBusinessPeriodList(@PathVariable("storeId") String storeId,
+ @AuthenticationPrincipal Merchant merchant,
+ @Validated @RequestBody List updateVoList) {
+ var storeIdLong = Long.parseLong(storeId);
+ return merchant.getStores().stream()
+ .filter(x -> Objects.equals(x.getId(), storeIdLong))
+ .findFirst()
+ .map(store -> {
+ store.getBusinessPeriods().clear();
+ for (BusinessPeriodVo businessPeriodVo : updateVoList) {
+ var entity = this.storeMapper.toEntity(businessPeriodVo);
+ store.addBusinessPeriods(entity);
+ this.businessPeriodRepository.save(entity);
+ }
+ return this.storeMapper.toDtoList(store.getBusinessPeriods());
+ })
+ .map(ApiResult::data)
+ .orElse(ApiResult.fail("门店信息不存在"));
+
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/controller/StoreEmployeeController.java b/src/main/java/com/xjhs/findmemerchant/controller/StoreEmployeeController.java
new file mode 100644
index 0000000..00d452e
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/controller/StoreEmployeeController.java
@@ -0,0 +1,157 @@
+package com.xjhs.findmemerchant.controller;
+
+import com.xjhs.findmemerchant.common.ApiResult;
+import com.xjhs.findmemerchant.dto.member.EmployeeDto;
+import com.xjhs.findmemerchant.entity.Merchant;
+import com.xjhs.findmemerchant.mapper.EmployeeMapper;
+import com.xjhs.findmemerchant.repository.EmployeeRepository;
+import com.xjhs.findmemerchant.vo.member.EmployeeCreateVo;
+import com.xjhs.findmemerchant.vo.member.EmployeeUpdateVo;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 门店员工管理接口
+ */
+@Slf4j
+@RestController
+@RequestMapping("/stores/{storeId}/employees")
+@RequiredArgsConstructor
+public class StoreEmployeeController {
+ private final EmployeeRepository employeeRepository;
+ private final EmployeeMapper employeeMapper;
+
+ /**
+ * 查询门店员工列表
+ * @param merchant @ignore 当前商户
+ * @param storeId 门店id
+ * @return 员工列表信息
+ */
+ @GetMapping
+ @Transactional(readOnly = true)
+ public ApiResult> getList(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") Long storeId) {
+ var list = this.employeeRepository.findByStoreId(storeId)
+ .stream().map(this.employeeMapper::toDto)
+ .toList();
+ return ApiResult.data(list);
+ }
+
+ /**
+ * 新建门店员工
+ * @param merchant @ignore 当前商户
+ * @param storeId 门店id
+ * @param createVo 员工信息参数
+ * @return 门店员工信息
+ */
+ @PostMapping
+ @Transactional
+ public ApiResult create(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") Long storeId,
+ @Validated @RequestBody EmployeeCreateVo createVo) throws Exception {
+ try {
+ var store = merchant.getStores().stream()
+ .filter(x -> Objects.equals(storeId, x.getId()))
+ .findFirst()
+ .orElseThrow(() -> new Exception("门店信息不存在"));
+ var phoneExists = store.getEmployees().stream()
+ .anyMatch(x -> Objects.equals(x.getPhone(), createVo.getPhone()));
+ if (phoneExists) {
+ throw new Exception("手机号码已被使用");
+ }
+ var employee = this.employeeMapper.toEntity(createVo);
+ store.addEmployee(employee);
+ this.employeeRepository.save(employee);
+ var dto = this.employeeMapper.toDto(employee);
+ return ApiResult.data(dto);
+ } catch (Exception e) {
+ log.error("员工信息注册失败", e);
+ throw e;
+ }
+ }
+
+ /**
+ * 查询门店员工详情
+ * @param merchant @ignore 当前商户
+ * @param storeId 门店id
+ * @param employeeId 门店员工id
+ * @return 员工详情
+ */
+ @GetMapping("/{employeeId}")
+ @Transactional(readOnly = true)
+ public ApiResult findById(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") Long storeId,
+ @PathVariable("employeeId") Long employeeId) {
+ return merchant.getStores().stream()
+ .filter(x -> Objects.equals(storeId, x.getId()))
+ .flatMap(x -> x.getEmployees().stream())
+ .filter(x -> Objects.equals(employeeId, x.getId()))
+ .findFirst()
+ .map(this.employeeMapper::toDto)
+ .map(ApiResult::data)
+ .orElse(ApiResult.fail("员工信息不存在"));
+
+ }
+
+ /**
+ * 根据员工id更新员工信息
+ * @param merchant @ignore 当前商户
+ * @param storeId 门店id
+ * @param employeeId 员工id
+ * @param updateVo 更新参数
+ * @return 员工详情
+ */
+ @PutMapping("/{employeeId}")
+ @Transactional
+ public ApiResult updateById(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") Long storeId,
+ @PathVariable("employeeId") Long employeeId,
+ @Validated @RequestBody EmployeeUpdateVo updateVo) {
+ return merchant.getStores().stream()
+ .filter(x -> Objects.equals(storeId, x.getId()))
+ .flatMap(x -> x.getEmployees().stream())
+ .filter(x -> Objects.equals(employeeId, x.getId()))
+ .findFirst()
+ .map(employee -> {
+ this.employeeMapper.updateEntityFormUpdateVo(updateVo, employee);
+ this.employeeRepository.save(employee);
+ return ApiResult.data(
+ this.employeeMapper.toDto(employee)
+ );
+ })
+ .orElse(ApiResult.fail("员工信息不存在"));
+ }
+
+ /**
+ * 根据id删除员工信息
+ * @param merchant @ignore 当前商户
+ * @param storeId 门店id
+ * @param employeeId 员工id
+ * @return 删除结果
+ */
+ @DeleteMapping("/{employeeId}")
+ @Transactional
+ public ApiResult deleteById(@AuthenticationPrincipal Merchant merchant,
+ @PathVariable("storeId") Long storeId,
+ @PathVariable("employeeId") Long employeeId) throws Exception {
+ try {
+ var store = merchant.getStores().stream()
+ .filter(x -> Objects.equals(storeId, x.getId()))
+ .findFirst()
+ .orElseThrow(()->new Exception("门店信息不存在"));
+ store.getEmployees().removeIf(x->Objects.equals(employeeId,x.getId()));
+ return ApiResult.success();
+ } catch (Exception e) {
+ log.error("删除员工失败",e);
+ throw e;
+ }
+ }
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/dto/MerchantDto.java b/src/main/java/com/xjhs/findmemerchant/dto/MerchantDto.java
new file mode 100644
index 0000000..1c235c3
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/dto/MerchantDto.java
@@ -0,0 +1,45 @@
+package com.xjhs.findmemerchant.dto;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.xjhs.findmemerchant.types.AuthStatus;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 商家信息
+ */
+@Data
+public class MerchantDto {
+ /**
+ * 商家id
+ */
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long id;
+ /**
+ * 手机号码
+ * TODO: 手机号码需要脱敏
+ */
+ private String phone;
+ /**
+ * 真实姓名
+ */
+ private String realName;
+ /**
+ * 身份证号(明文)
+ */
+ private String idCardNo;
+ /**
+ * 认证状态
+ */
+ private AuthStatus authStatus;
+ /**
+ * 认证状态(说明)
+ */
+ private String authStatusDesc;
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createdAt;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/dto/auth/RegisterDto.java b/src/main/java/com/xjhs/findmemerchant/dto/auth/RegisterDto.java
index 8c2ee41..6a415d8 100644
--- a/src/main/java/com/xjhs/findmemerchant/dto/auth/RegisterDto.java
+++ b/src/main/java/com/xjhs/findmemerchant/dto/auth/RegisterDto.java
@@ -16,7 +16,8 @@ public class RegisterDto {
/**
* 商家id
*/
- private String merchantId;
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long merchantId;
/**
* 访问令牌
*/
diff --git a/src/main/java/com/xjhs/findmemerchant/dto/member/EmployeeDto.java b/src/main/java/com/xjhs/findmemerchant/dto/member/EmployeeDto.java
new file mode 100644
index 0000000..d592180
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/dto/member/EmployeeDto.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.dto.member;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class EmployeeDto {
+
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long id;
+ /**
+ * 员工姓名
+ */
+ private String name;
+ /**
+ * 手机号码
+ */
+ private String phone;
+ /**
+ * 关联角色id
+ */
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long roleId;
+ /**
+ * 关联门店id
+ */
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long storeId;
+ /**
+ * 员工状态
+ */
+ private Integer status;
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createdAt;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/dto/merchant/MerchantDto.java b/src/main/java/com/xjhs/findmemerchant/dto/merchant/MerchantDto.java
deleted file mode 100644
index 5742c20..0000000
--- a/src/main/java/com/xjhs/findmemerchant/dto/merchant/MerchantDto.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.xjhs.findmemerchant.dto.merchant;
-
-import lombok.Data;
-
-@Data
-public class MerchantDto {
- private String id;
- private String merchantName;
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/dto/store/BusinessPeriodDto.java b/src/main/java/com/xjhs/findmemerchant/dto/store/BusinessPeriodDto.java
new file mode 100644
index 0000000..c7201d8
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/dto/store/BusinessPeriodDto.java
@@ -0,0 +1,30 @@
+package com.xjhs.findmemerchant.dto.store;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import lombok.Data;
+
+@Data
+public class BusinessPeriodDto {
+ /**
+ * 门店id
+ */
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long storeId;
+ /**
+ * 周几,周天0,...
+ */
+ private Integer dayOfWeek;
+ /**
+ * 开始营业时间 HH:mm 格式
+ */
+ private String startTime;
+ /**
+ * 结束营业时间 HH:mm 格式
+ */
+ private String endTime;
+ /**
+ * 是否启用
+ */
+ private Boolean enabled;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/dto/store/StoreBusinessStatusDto.java b/src/main/java/com/xjhs/findmemerchant/dto/store/StoreBusinessStatusDto.java
new file mode 100644
index 0000000..67c1533
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/dto/store/StoreBusinessStatusDto.java
@@ -0,0 +1,36 @@
+package com.xjhs.findmemerchant.dto.store;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 门店营业状态
+ */
+@Data
+public class StoreBusinessStatusDto {
+ /**
+ * 业务状态值
+ */
+ private Integer status;
+
+ /**
+ * 业务状态文案
+ */
+ private String statusText;
+
+ /**
+ * 临时关闭原因(可为空)
+ */
+ private String tempCloseReason;
+
+ /**
+ * 临时关闭截止时间(可为空)
+ */
+ private LocalDateTime tempCloseUntil;
+
+ /**
+ * 是否营业
+ */
+ private Boolean isOpen;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/dto/store/StoreDto.java b/src/main/java/com/xjhs/findmemerchant/dto/store/StoreDto.java
new file mode 100644
index 0000000..563a993
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/dto/store/StoreDto.java
@@ -0,0 +1,110 @@
+package com.xjhs.findmemerchant.dto.store;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.xjhs.findmemerchant.types.BusinessStatus;
+import com.xjhs.findmemerchant.types.CommonStatus;
+import com.xjhs.findmemerchant.types.StoreAuditStatus;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class StoreDto {
+ /**
+ * 门店id
+ */
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long id;
+ /**
+ * 商家id
+ */
+ @JsonSerialize(using = ToStringSerializer.class)
+ private Long merchantId;
+ /**
+ * 门店名称
+ */
+ private String name;
+ /**
+ * 门店logo
+ */
+ private String logo;
+ /**
+ * 联系电话
+ */
+ private String phone;
+ /**
+ * 省
+ */
+ private String province;
+ /**
+ * 市
+ */
+ private String city;
+ /**
+ * 区/县
+ */
+ private String district;
+ /**
+ * 详细地址
+ */
+ private String address;
+ /**
+ * 完整地址
+ */
+ private String fullAddress;
+
+ // TODO: 后续改为 Point location
+ /**
+ * 经度
+ */
+ private BigDecimal longitude;
+ /**
+ * 纬度
+ */
+ private BigDecimal latitude;
+ /**
+ * 营业时间描述
+ */
+ private String businessHours;
+ /**
+ * 营业状态
+ */
+ private BusinessStatus businessStatus;
+ /**
+ * 营业状态(说明)
+ */
+ private String businessStatusDesc;
+ /**
+ * 临时打烊原因
+ */
+ private String tempCloseReason;
+ /**
+ * 临时打烊结束时间
+ */
+ private String tempCloseUntil;
+ /**
+ * 审核状态
+ */
+ private StoreAuditStatus auditStatus;
+ /**
+ * 审核状态(说明)
+ */
+ private String auditStatusDesc;
+ /**
+ * 审核备注
+ */
+ private String auditRemark;
+ /**
+ * 启用状态
+ */
+ private CommonStatus status;
+ /**
+ * 启用状态(说明)
+ */
+ private String statusDesc;
+ /**
+ * 创建时间
+ */
+ private String createdAt;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Activity.java b/src/main/java/com/xjhs/findmemerchant/entity/Activity.java
new file mode 100644
index 0000000..746fc32
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Activity.java
@@ -0,0 +1,137 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.ActivityStatus;
+import com.xjhs.findmemerchant.types.ActivityType;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.time.LocalDateTime;
+
+/**
+ * 活动 / 营销活动实体
+ * 对应表:activities
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "activities",
+ indexes = {
+ @Index(name = "idx_activities_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_activities_time", columnList = "start_time,end_time"),
+ @Index(name = "idx_activities_status", columnList = "status")
+ }
+)
+public class Activity extends AbstractBaseEntity {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id")
+ private Merchant merchant;
+
+ /**
+ * 活动名称
+ */
+ @Column(name = "name", length = 100)
+ @Comment("活动名称")
+ private String name;
+
+ /**
+ * 活动类型 (1团购 2折扣 3限时优惠)
+ */
+ @Column(name = "type", length = 15, columnDefinition = "VARCHAR(15)")
+ @Enumerated(EnumType.STRING)
+ @Comment("活动类型")
+ private ActivityType type;
+
+ /**
+ * 开始时间
+ */
+ @Column(name = "start_time")
+ @Comment("开始时间")
+ private LocalDateTime startTime;
+
+ /**
+ * 结束时间
+ */
+ @Column(name = "end_time")
+ @Comment("结束时间")
+ private LocalDateTime endTime;
+
+ /**
+ * 库存
+ */
+ @Column(name = "stock")
+ @Comment("库存总数")
+ private Integer stock = 0;
+
+ /**
+ * 已售数量
+ */
+ @Column(name = "sold_count")
+ @Comment("已售数量")
+ private Integer soldCount = 0;
+
+ /**
+ * 库存告警阈值
+ */
+ @Column(name = "alert_threshold")
+ @Comment("库存告警阈值")
+ private Integer alertThreshold;
+
+ /**
+ * 活动状态
+ */
+ @Column(name = "status", columnDefinition = "VARCHAR(20)")
+ @Enumerated(EnumType.STRING)
+ @Comment("活动状态:0未开始 1进行中 2已结束 3已下架")
+ private ActivityStatus status = ActivityStatus.NOT_STARTED;
+
+
+
+
+ // ================= 业务逻辑 =================
+
+ public boolean isOngoing() {
+ var now = LocalDateTime.now();
+ return this.status == ActivityStatus.ONGOING
+ && now.isAfter(startTime)
+ && now.isBefore(endTime);
+ }
+
+ public boolean hasStock() {
+ return stock != null && soldCount != null && soldCount < stock;
+ }
+
+ public int remainingStock() {
+ if (stock == null || soldCount == null) return 0;
+ return stock - soldCount;
+ }
+
+ public boolean isLowStock() {
+ return alertThreshold != null && remainingStock() <= alertThreshold;
+ }
+
+ public String getTypeText() {
+ var e = this.type;
+ return e != null ? e.getDesc() : "未知";
+ }
+
+ public String getStatusText() {
+ var e = this.status;
+ return e != null ? e.getDesc() : "未知";
+ }
+
+ public boolean canEdit() {
+ return this.status == ActivityStatus.NOT_STARTED;
+ }
+
+ public boolean canOffline() {
+ var s = this.status;
+ return s == ActivityStatus.NOT_STARTED || s == ActivityStatus.ONGOING;
+ }
+
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/BankCard.java b/src/main/java/com/xjhs/findmemerchant/entity/BankCard.java
new file mode 100644
index 0000000..1f26233
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/BankCard.java
@@ -0,0 +1,109 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.CommonStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+/**
+ * 商家提现银行卡
+ * 对应表:bank_cards
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "bank_cards",
+ indexes = {
+ @Index(name = "idx_bank_cards_merchant_id", columnList = "merchant_id")
+ }
+)
+public class BankCard extends AbstractBaseEntity {
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ /**
+ * 银行名称
+ */
+ @Column(name = "bank_name", length = 50)
+ @Comment("银行名称")
+ private String bankName;
+
+ /**
+ * 银行代码
+ */
+ @Column(name = "bank_code", length = 20)
+ @Comment("银行代码")
+ private String bankCode;
+
+ /**
+ * 支行名称
+ */
+ @Column(name = "branch_name", length = 100)
+ @Comment("支行名称")
+ private String branchName;
+
+ /**
+ * 银行账号(建议加密存储)
+ */
+ @Column(name = "account_no", length = 30)
+ @Comment("银行卡号(加密存储)")
+ private String accountNo;
+
+ /**
+ * 开户名
+ */
+ @Column(name = "account_name", length = 50)
+ @Comment("开户名")
+ private String accountName;
+
+ /**
+ * 是否默认卡
+ */
+ @Column(name = "is_default")
+ @Comment("是否默认卡")
+ private Boolean isDefault = false;
+
+ /**
+ * 状态:0-禁用 1-启用
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(15)",length = 15)
+ @Enumerated(EnumType.STRING)
+ @Comment("状态:0禁用 1启用")
+ private CommonStatus status = CommonStatus.ENABLED;
+
+
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 脱敏账号,例如:6222****1234
+ */
+ public String maskAccountNo() {
+ if (accountNo == null || accountNo.length() <= 8) {
+ return accountNo;
+ }
+ return accountNo.substring(0, 4)
+ + "****"
+ + accountNo.substring(accountNo.length() - 4);
+ }
+
+ /**
+ * 是否启用
+ */
+ public boolean isActive() {
+ return this.status == CommonStatus.ENABLED;
+ }
+
+ public String getStatusText() {
+ CommonStatus e = this.status;
+ return e != null ? e.getDesc() : "未知";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/BusinessLicense.java b/src/main/java/com/xjhs/findmemerchant/entity/BusinessLicense.java
new file mode 100644
index 0000000..03b579e
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/BusinessLicense.java
@@ -0,0 +1,67 @@
+package com.xjhs.findmemerchant.entity;
+
+
+import com.xjhs.findmemerchant.common.jpa.json.HashMapJsonConverter;
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.BusinessLicenseStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.util.HashMap;
+
+/**
+ * 营业执照资质
+ * 对应表:business_licenses
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "business_licenses",
+ indexes = {
+ @Index(name = "idx_business_licenses_merchant_id", columnList = "merchant_id")
+ }
+)
+public class BusinessLicense extends AbstractBaseEntity {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ @Column(name = "image_url", length = 500)
+ @Comment("营业执照图片URL")
+ private String imageUrl;
+
+ @Column(name = "company_name", length = 200)
+ @Comment("公司名称(OCR识别)")
+ private String companyName;
+
+ @Column(name = "license_no", length = 50)
+ @Comment("营业执照号(OCR识别)")
+ private String licenseNo;
+
+ @Convert(converter = HashMapJsonConverter.class)
+ @Column(name = "ocr_raw", columnDefinition = "json")
+ @Comment("OCR原始结果")
+ private HashMap ocrRaw;
+
+ @Column(name = "status",length = 15,columnDefinition = "VARCHAR(15)")
+ @Enumerated(EnumType.STRING)
+ @Comment("状态:0待审核 1已通过 2已拒绝")
+ private BusinessLicenseStatus status = BusinessLicenseStatus.PENDING;
+
+
+ // ===== 业务方法 =====
+
+ public boolean isApproved() {
+ return this.status == BusinessLicenseStatus.APPROVED;
+ }
+
+
+ public String getStatusText() {
+ var e = this.status;
+ return e != null ? e.getDesc() : "未知";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/BusinessPeriod.java b/src/main/java/com/xjhs/findmemerchant/entity/BusinessPeriod.java
new file mode 100644
index 0000000..489b74f
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/BusinessPeriod.java
@@ -0,0 +1,81 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+/**
+ * 门店营业时间段
+ * 对应表:business_periods
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "business_periods",
+ indexes = {
+ @Index(name = "idx_business_periods_store_id", columnList = "store_id")
+ }
+)
+public class BusinessPeriod extends AbstractBaseEntity {
+
+ /**
+ * 门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "store_id", insertable = false, updatable = false)
+ @Comment("门店Id")
+ private Store store;
+
+ /**
+ * 周几:0=周日 ... 6=周六
+ */
+ @Column(name = "day_of_week")
+ @Comment("周几:0周日 1周一 ... 6周六")
+ private Integer dayOfWeek;
+
+ /**
+ * 开始时间,HH:MM
+ */
+ @Column(name = "start_time", length = 5)
+ @Comment("开始时间,HH:MM")
+ private String startTime;
+
+ /**
+ * 结束时间,HH:MM
+ */
+ @Column(name = "end_time", length = 5)
+ @Comment("结束时间,HH:MM")
+ private String endTime;
+
+ /**
+ * 是否启用
+ */
+ @Column(name = "is_enabled")
+ @Comment("是否启用")
+ private Boolean isEnabled = Boolean.TRUE;
+
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 中文周几,比如:周一
+ */
+ public String getDayName() {
+ if (dayOfWeek == null) {
+ return "未知";
+ }
+ return switch (dayOfWeek) {
+ case 0 -> "周日";
+ case 1 -> "周一";
+ case 2 -> "周二";
+ case 3 -> "周三";
+ case 4 -> "周四";
+ case 5 -> "周五";
+ case 6 -> "周六";
+ default -> "未知";
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Coupon.java b/src/main/java/com/xjhs/findmemerchant/entity/Coupon.java
new file mode 100644
index 0000000..5f8b1d7
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Coupon.java
@@ -0,0 +1,211 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.CouponStatus;
+import com.xjhs.findmemerchant.types.CouponType;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 优惠券模板实体
+ * 对应表:coupons
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "coupons",
+ indexes = {
+ @Index(name = "idx_coupons_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_coupons_status", columnList = "status"),
+ @Index(name = "idx_coupons_gift_product_id", columnList = "gift_product_id")
+ }
+)
+public class Coupon extends AbstractBaseEntity {
+
+ /**
+ * 优惠券名称
+ */
+ @Column(name = "name", length = 100)
+ @Comment("优惠券名称")
+ private String name;
+
+ /**
+ * 优惠券类型:1-折扣券 2-满减券 3-现金券 4-赠品券
+ */
+ @Column(name = "type",length = 15,columnDefinition = "VARCHAR(15)")
+ @Comment("优惠券类型:1折扣券 2满减券 3现金券 4赠品券")
+ private CouponType type;
+
+ /**
+ * 折扣率 0.01-0.99,decimal(3,2)
+ */
+ @Column(name = "discount_rate", precision = 3, scale = 2)
+ @Comment("折扣率 0.01-0.99")
+ private BigDecimal discountRate;
+
+ /**
+ * 满减门槛,decimal(10,2)
+ */
+ @Column(name = "min_amount", precision = 10, scale = 2)
+ @Comment("满减门槛")
+ private BigDecimal minAmount;
+
+ /**
+ * 减免金额,decimal(10,2)
+ */
+ @Column(name = "reduce_amount", precision = 10, scale = 2)
+ @Comment("减免金额")
+ private BigDecimal reduceAmount;
+
+ /**
+ * 赠品商品ID
+ */
+ @Column(name = "gift_product_id")
+ @Comment("赠品商品ID")
+ private Long giftProductId;
+
+ /**
+ * 赠品数量,默认 1
+ */
+ @Column(name = "gift_quantity")
+ @Comment("赠品数量")
+ private Integer giftQuantity = 1;
+
+ /**
+ * 发放总量
+ */
+ @Column(name = "total_count")
+ @Comment("发放总量")
+ private Integer totalCount;
+
+ /**
+ * 已领取数量
+ */
+ @Column(name = "claimed_count")
+ @Comment("已领取数量")
+ private Integer claimedCount = 0;
+
+ /**
+ * 每人限领数量,0 表示不限
+ */
+ @Column(name = "per_user_limit")
+ @Comment("每人限领数量,0表示不限")
+ private Integer perUserLimit = 1;
+
+ /**
+ * 领取后有效天数
+ */
+ @Column(name = "valid_days")
+ @Comment("领取后有效天数")
+ private Integer validDays;
+
+ /**
+ * 活动开始时间
+ */
+ @Column(name = "start_time")
+ @Comment("活动开始时间")
+ private LocalDateTime startTime;
+
+ /**
+ * 活动结束时间
+ */
+ @Column(name = "end_time")
+ @Comment("活动结束时间")
+ private LocalDateTime endTime;
+
+ /**
+ * 使用规则
+ */
+ @Column(name = "rules", columnDefinition = "text")
+ @Comment("使用规则")
+ private String rules;
+
+ /**
+ * 状态:0-下架 1-进行中 2-已结束
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(15)",length = 15)
+ @Comment("状态:0下架 1进行中 2已结束")
+ private CouponStatus status = CouponStatus.ONLINE;
+
+
+ // ================= 关联关系 =================
+
+ /**
+ * 所属商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ /**
+ * 可用门店关联
+ */
+ @OneToMany(mappedBy = "coupon", fetch = FetchType.LAZY)
+ private List stores;
+
+ /**
+ * 所有券码
+ */
+ @OneToMany(mappedBy = "coupon", fetch = FetchType.LAZY)
+ private List codes;
+
+ /**
+ * 赠品商品
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "gift_product_id", insertable = false, updatable = false)
+ private Product giftProduct;
+
+ // ================= 业务方法 =================
+
+ /**
+ * 是否当前有效(进行中 + 时间范围内)
+ */
+ public boolean isActive() {
+ LocalDateTime now = LocalDateTime.now();
+ return this.status == CouponStatus.ONLINE
+ && now.isAfter(startTime)
+ && now.isBefore(endTime);
+ }
+
+ /**
+ * 是否还有库存可领取
+ */
+ public boolean hasStock() {
+ return claimedCount != null && totalCount != null && claimedCount < totalCount;
+ }
+
+ /**
+ * 剩余可领取数量
+ */
+ public int remainingCount() {
+ if (totalCount == null || claimedCount == null) {
+ return 0;
+ }
+ return totalCount - claimedCount;
+ }
+
+ /**
+ * 优惠券类型文案
+ */
+ public String getTypeText() {
+ CouponType typeEnum = this.type;
+ return typeEnum != null ? typeEnum.getDesc() : "未知";
+ }
+
+ /**
+ * 是否为赠品券
+ */
+ public boolean isGiftCoupon() {
+ return this.type == CouponType.GIFT;
+ }
+
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/CouponCode.java b/src/main/java/com/xjhs/findmemerchant/entity/CouponCode.java
new file mode 100644
index 0000000..fe78cc2
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/CouponCode.java
@@ -0,0 +1,172 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.CouponCodeStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.time.LocalDateTime;
+
+/**
+ * 优惠券码实例
+ * 对应表:coupon_codes
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "coupon_codes",
+ indexes = {
+ @Index(name = "idx_coupon_codes_coupon_id", columnList = "coupon_id"),
+ @Index(name = "idx_coupon_codes_member_id", columnList = "member_id"),
+ @Index(name = "idx_coupon_codes_status", columnList = "status")
+ },
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_coupon_codes_code", columnNames = "code")
+ }
+)
+public class CouponCode extends AbstractBaseEntity {
+
+
+ /**
+ * 券码
+ */
+ @Column(name = "code", length = 32)
+ @Comment("优惠券码")
+ private String code;
+
+ /**
+ * 状态:0-未领取 1-已领取 2-已核销 3-已过期
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(20)",length = 20)
+ @Comment("状态:0未领取 1已领取 2已核销 3已过期")
+ private CouponCodeStatus status = CouponCodeStatus.UNCLAIMED;
+
+ /**
+ * 领取时间
+ */
+ @Column(name = "claimed_at")
+ @Comment("领取时间")
+ private LocalDateTime claimedAt;
+
+ /**
+ * 有效期至
+ */
+ @Column(name = "valid_until")
+ @Comment("有效期至")
+ private LocalDateTime validUntil;
+
+ /**
+ * 核销时间
+ */
+ @Column(name = "verified_at")
+ @Comment("核销时间")
+ private LocalDateTime verifiedAt;
+
+ /**
+ * 核销人ID(员工)
+ */
+ @Column(name = "verified_by")
+ @Comment("核销人ID(员工)")
+ private Long verifiedBy;
+
+ /**
+ * 核销门店ID
+ */
+ @Column(name = "verify_store_id")
+ @Comment("核销门店ID")
+ private Long verifyStoreId;
+
+
+ // =============== 关联关系 ===============
+
+ /**
+ * 所属优惠券模板
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "coupon_id", insertable = false, updatable = false)
+ private Coupon coupon;
+
+ /**
+ * 会员
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "member_id", insertable = false, updatable = false)
+ private Member member;
+
+ /**
+ * 核销员工
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "verified_by", insertable = false, updatable = false)
+ private Employee verifier;
+
+ /**
+ * 核销门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "verify_store_id", insertable = false, updatable = false)
+ private Store verifyStore;
+
+ // =============== 业务方法 ===============
+
+ /**
+ * 是否已领取
+ */
+ public boolean isClaimed() {
+ return this.status != null &&
+ this.status.ordinal() >= CouponCodeStatus.CLAIMED.ordinal();
+ }
+
+ /**
+ * 是否已核销
+ */
+ public boolean isVerified() {
+ return this.status == CouponCodeStatus.VERIFIED;
+ }
+
+ /**
+ * 是否已过期
+ */
+ public boolean isExpired() {
+ CouponCodeStatus statusEnum = this.status;
+ if (statusEnum == CouponCodeStatus.EXPIRED) {
+ return true;
+ }
+ if (validUntil != null && LocalDateTime.now().isAfter(validUntil)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 是否可核销
+ */
+ public boolean canVerify() {
+ return this.status == CouponCodeStatus.CLAIMED && !isExpired();
+ }
+
+ /**
+ * 状态文案
+ */
+ public String getStatusText() {
+ CouponCodeStatus statusEnum = this.status;
+ return statusEnum != null ? statusEnum.getDesc() : "未知";
+ }
+
+ /**
+ * 脱敏券码,例如:ABC***XYZ
+ */
+ public String maskCode() {
+ if (code == null) {
+ return null;
+ }
+ if (code.length() <= 6) {
+ return code;
+ }
+ return code.substring(0, 3) + "***" + code.substring(code.length() - 3);
+ }
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/CouponStore.java b/src/main/java/com/xjhs/findmemerchant/entity/CouponStore.java
new file mode 100644
index 0000000..2031490
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/CouponStore.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 优惠券与门店的关联关系
+ * 对应表:coupon_stores
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "coupon_stores",
+ uniqueConstraints = {
+ @UniqueConstraint(
+ name = "idx_coupon_store",
+ columnNames = {"coupon_id", "store_id"}
+ )
+ }
+)
+public class CouponStore extends AbstractBaseEntity {
+
+
+ /**
+ * 优惠券
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "coupon_id", insertable = false, updatable = false)
+ private Coupon coupon;
+
+ /**
+ * 门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "store_id", insertable = false, updatable = false)
+ private Store store;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Employee.java b/src/main/java/com/xjhs/findmemerchant/entity/Employee.java
new file mode 100644
index 0000000..9350ab8
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Employee.java
@@ -0,0 +1,95 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.CommonStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+/**
+ * 员工实体
+ * 对应表:employees
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "employees",
+ indexes = {
+ @Index(name = "idx_employees_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_employees_store_id", columnList = "store_id"),
+ @Index(name = "idx_employees_role_id", columnList = "role_id"),
+ @Index(name = "idx_employees_phone", columnList = "phone")
+ }
+)
+public class Employee extends AbstractBaseEntity {
+
+
+ /**
+ * 手机号
+ */
+ @Column(name = "phone", length = 11)
+ @Comment("手机号")
+ private String phone;
+
+ /**
+ * 员工姓名
+ */
+ @Column(name = "name", length = 50)
+ @Comment("员工姓名")
+ private String name;
+
+ /**
+ * 状态:0-禁用 1-启用
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(15)",length = 15)
+ @Comment("状态")
+ private CommonStatus status = CommonStatus.ENABLED;
+
+
+
+ // ===== 关联关系 =====
+ /**
+ * 门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "store_id", insertable = false, updatable = false)
+ private Store store;
+
+ /**
+ * 角色
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "role_id", insertable = false, updatable = false)
+ private Role role;
+
+ // ===== 业务方法 =====
+
+ /**
+ * 是否为启用状态
+ */
+ public boolean isActive() {
+ return this.status == CommonStatus.ENABLED;
+ }
+
+
+
+ /**
+ * 状态文案
+ */
+ public String getStatusText() {
+ CommonStatus e = this.status;
+ return e != null ? e.getDesc() : "未知";
+ }
+
+ /**
+ * 返回脱敏手机号,例如:138****1234
+ */
+ public String maskPhone() {
+ if (phone == null || phone.length() != 11) {
+ return phone;
+ }
+ return phone.substring(0, 3) + "****" + phone.substring(7);
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/HealthCertificate.java b/src/main/java/com/xjhs/findmemerchant/entity/HealthCertificate.java
new file mode 100644
index 0000000..b0111a9
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/HealthCertificate.java
@@ -0,0 +1,90 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.json.HashMapJsonConverter;
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.HealthCertificateStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+
+/**
+ * 健康证资质
+ * 对应表:health_certificates
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "health_certificates",
+ indexes = {
+ @Index(name = "idx_health_certificates_store_id", columnList = "store_id"),
+ @Index(name = "idx_health_certificates_employee_id", columnList = "employee_id")
+ }
+)
+public class HealthCertificate extends AbstractBaseEntity {
+
+ @Column(name = "image_url", length = 500)
+ @Comment("健康证图片URL")
+ private String imageUrl;
+
+ @Column(name = "holder_name", length = 50)
+ @Comment("持证人姓名(OCR识别)")
+ private String holderName;
+
+ @Column(name = "valid_until")
+ @Comment("证件有效期至(OCR识别)")
+ private LocalDateTime validUntil;
+
+ @Column(name = "issuer", length = 100)
+ @Comment("签发机构(OCR识别)")
+ private String issuer;
+
+ @Convert(converter = HashMapJsonConverter.class)
+ @Column(name = "ocr_raw", columnDefinition = "json")
+ @Comment("OCR原始结果")
+ private HashMap ocrRaw;
+
+ @Column(name = "status",columnDefinition = "VARCHAR(20)",length = 20)
+ @Comment("状态:0待审核 1有效 2过期")
+ private HealthCertificateStatus status = HealthCertificateStatus.PENDING;
+
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "store_id", insertable = false, updatable = false)
+ private Store store;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "employee_id", insertable = false, updatable = false)
+ private Employee employee;
+
+ // ===== 业务方法 =====
+
+ public boolean isValid() {
+ if (getStatusEnum() != HealthCertificateStatus.VALID) {
+ return false;
+ }
+ if (validUntil != null && LocalDateTime.now().isAfter(validUntil)) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isExpired() {
+ if (validUntil == null) return false;
+ return LocalDateTime.now().isAfter(validUntil);
+ }
+
+ public HealthCertificateStatus getStatusEnum() {
+ return this.status;
+ }
+
+
+ public String getStatusText() {
+ var e = getStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Member.java b/src/main/java/com/xjhs/findmemerchant/entity/Member.java
new file mode 100644
index 0000000..994abee
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Member.java
@@ -0,0 +1,127 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 会员实体(C 端用户)
+ * 对应表:members
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "members",
+ indexes = {
+ @Index(name = "idx_members_phone", columnList = "phone")
+ },
+ uniqueConstraints = {
+ @UniqueConstraint(
+ name = "idx_merchant_phone",
+ columnNames = {"merchant_id", "phone"}
+ )
+ }
+)
+public class Member extends AbstractBaseEntity {
+
+ /**
+ * 手机号
+ */
+ @Column(name = "phone", length = 11)
+ @Comment("手机号")
+ private String phone;
+
+ /**
+ * 昵称
+ */
+ @Column(name = "nickname", length = 50)
+ @Comment("昵称")
+ private String nickname;
+
+ /**
+ * 头像地址
+ */
+ @Column(name = "avatar", length = 500)
+ @Comment("头像URL")
+ private String avatar;
+
+ /**
+ * 累计订单数
+ */
+ @Column(name = "total_orders")
+ @Comment("累计订单数")
+ private Integer totalOrders = 0;
+
+ /**
+ * 累计消费金额
+ */
+ @Column(name = "total_amount", precision = 12, scale = 2)
+ @Comment("累计消费金额")
+ private BigDecimal totalAmount = BigDecimal.ZERO;
+
+ /**
+ * 最后一次下单时间
+ */
+ @Column(name = "last_order_at")
+ @Comment("最后下单时间")
+ private LocalDateTime lastOrderAt;
+
+ // ============ 关联关系 ============
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ /**
+ * 拥有的券码
+ */
+ @OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
+ private List couponCodes;
+
+ /**
+ * 订单列表
+ */
+ @OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
+ private List orders;
+
+ // ============ 业务方法 ============
+
+ /**
+ * 返回脱敏手机号,例如:138****1234
+ */
+ public String maskPhone() {
+ if (phone == null || phone.length() != 11) {
+ return phone;
+ }
+ return phone.substring(0, 3) + "****" + phone.substring(7);
+ }
+
+ /**
+ * 展示用名称:优先昵称,否则使用脱敏手机号
+ */
+ public String getDisplayName() {
+ if (nickname != null && !nickname.isBlank()) {
+ return nickname;
+ }
+ return maskPhone();
+ }
+
+ /**
+ * 返回头像URL,若为空则返回默认头像
+ */
+ public String getAvatarOrDefault() {
+ if (avatar != null && !avatar.isBlank()) {
+ return avatar;
+ }
+ return "/static/default-avatar.png";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Merchant.java b/src/main/java/com/xjhs/findmemerchant/entity/Merchant.java
index e10539a..15a4bc2 100644
--- a/src/main/java/com/xjhs/findmemerchant/entity/Merchant.java
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Merchant.java
@@ -1,27 +1,147 @@
package com.xjhs.findmemerchant.entity;
+
import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
-import com.xjhs.findmemerchant.system.entity.SystemUser;
-import jakarta.persistence.Entity;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.OneToOne;
-import jakarta.persistence.Table;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import com.xjhs.findmemerchant.types.AuthStatus;
+import com.xjhs.findmemerchant.types.CommonStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
import org.hibernate.annotations.Comment;
-@EqualsAndHashCode(callSuper = true)
-@Data
-@Entity
-@Table
-@Comment("商户信息表")
-public class Merchant extends AbstractBaseEntity {
- /**
- * 用户信息
- */
- @OneToOne
- @JoinColumn(name = "user_id")
- @Comment("关联的用户信息")
- private SystemUser systemUser;
+import java.util.List;
+/**
+ * 商家实体
+ * 对应表:merchants
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "merchants",
+ indexes = {
+ @Index(name = "idx_merchants_auth_status", columnList = "auth_status"),
+ @Index(name = "idx_merchants_status", columnList = "status")
+ },
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_merchant_phone", columnNames = "phone")
+ }
+)
+public class Merchant extends AbstractBaseEntity {
+
+
+ /**
+ * 登录手机号
+ */
+ @Column(name = "phone", length = 11)
+ @Comment("手机号")
+ private String phone;
+
+ /**
+ * 登录密码Hash
+ */
+ @Column(name = "password_hash", length = 255)
+ @Comment("密码Hash")
+ private String passwordHash;
+
+ /**
+ * 真实姓名
+ */
+ @Column(name = "real_name", length = 50)
+ @Comment("真实姓名")
+ private String realName;
+
+ /**
+ * 身份证号(明文)——不入库
+ */
+ @Transient
+ private String idCardNo;
+
+ /**
+ * 身份证加密存储
+ */
+ @Column(name = "id_card_encrypted", length = 255)
+ @Comment("身份证加密存储")
+ private String idCardEncrypted;
+
+ /**
+ * 认证状态
+ */
+ @Column(name = "auth_status",length = 20,columnDefinition = "VARCHAR(20)")
+ @Comment("认证状态")
+ @Enumerated(EnumType.STRING)
+ private AuthStatus authStatus = AuthStatus.NOT_VERIFIED;
+
+ /**
+ * 账户状态
+ */
+ @Column(name = "status",length = 20,columnDefinition = "VARCHAR(20)")
+ @Comment("账户状态")
+ @Enumerated(EnumType.STRING)
+ private CommonStatus status = CommonStatus.ENABLED;
+
+ // ========== 关联关系 ==========
+
+ /**
+ * 门店列表
+ */
+ @OneToMany(mappedBy = "merchant", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
+ private List stores;
+
+ /**
+ * 添加一个门店
+ * @param store 门店信息
+ */
+ public void addStore(Store store) {
+ store.setMerchant(this);
+ this.stores.add(store);
+ }
+
+
+ /**
+ * 营业执照
+ */
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "id", referencedColumnName = "merchant_id", insertable = false, updatable = false)
+ private BusinessLicense businessLicense;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 是否已完成实名认证
+ */
+ public boolean isAuthenticated() {
+ return getAuthStatusEnum() == AuthStatus.VERIFIED;
+ }
+
+ public AuthStatus getAuthStatusEnum() {
+ return this.authStatus;
+ }
+ /**
+ * 是否启用
+ */
+ public boolean isActive() {
+ return getStatusEnum() == CommonStatus.ENABLED;
+ }
+
+ public CommonStatus getStatusEnum() {
+ return this.status;
+ }
+
+ /**
+ * 手机脱敏
+ */
+ public String maskPhone() {
+ if (phone == null || phone.length() != 11) return phone;
+ return phone.substring(0, 3) + "****" + phone.substring(7);
+ }
+
+ /**
+ * 身份证脱敏
+ */
+ public String maskIdCard() {
+ if (idCardNo == null || idCardNo.length() != 18) return idCardNo;
+ return idCardNo.substring(0, 3) + "***********" + idCardNo.substring(14);
+ }
}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Message.java b/src/main/java/com/xjhs/findmemerchant/entity/Message.java
new file mode 100644
index 0000000..baf6965
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Message.java
@@ -0,0 +1,122 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.MessageType;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.time.LocalDateTime;
+
+/**
+ * 消息实体
+ * 对应表:messages
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "messages",
+ indexes = {
+ @Index(name = "idx_messages_merchant", columnList = "merchant_id"),
+ @Index(name = "idx_messages_type_read", columnList = "merchant_id,type,is_read")
+ }
+)
+public class Message extends AbstractBaseEntity {
+
+ /**
+ * 消息类型:1-系统通知 2-活动提醒 3-私信
+ */
+ @Column(name = "type",columnDefinition = "VARCHAR(20)",length = 20)
+ @Enumerated(EnumType.STRING)
+ @Comment("消息类型:1系统通知 2活动提醒 3私信")
+ private MessageType type;
+
+ /**
+ * 标题
+ */
+ @Column(name = "title", length = 200)
+ @Comment("消息标题")
+ private String title;
+
+ /**
+ * 内容
+ */
+ @Column(name = "content", columnDefinition = "text")
+ @Comment("消息内容")
+ private String content;
+
+ /**
+ * 是否已读:0-未读 1-已读
+ */
+ @Column(name = "is_read")
+ @Comment("是否已读:0未读 1已读")
+ private Boolean isRead = false;
+
+ /**
+ * 创建时间
+ */
+ @Column(name = "created_at")
+ @Comment("创建时间")
+ private LocalDateTime createdAt;
+
+ /**
+ * 阅读时间
+ */
+ @Column(name = "read_at")
+ @Comment("阅读时间")
+ private LocalDateTime readAt;
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 标记为已读
+ */
+ public void markAsRead() {
+ this.isRead = true;
+ this.readAt = LocalDateTime.now();
+ }
+
+ /**
+ * 是否未读
+ */
+ public boolean isUnread() {
+ return this.isRead != null && !this.isRead;
+ }
+
+ /**
+ * 消息类型文案
+ */
+ public String getTypeText() {
+ MessageType t = getTypeEnum();
+ return t != null ? t.getDesc() : "未知";
+ }
+
+ /**
+ * 内容摘要,最多100字符
+ */
+ public String getSummary() {
+ if (content == null) {
+ return "";
+ }
+ if (content.length() <= 100) {
+ return content;
+ }
+ return content.substring(0, 100) + "...";
+ }
+
+ // ========== 枚举转换辅助 ==========
+
+ public MessageType getTypeEnum() {
+ return this.type;
+ }
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Order.java b/src/main/java/com/xjhs/findmemerchant/entity/Order.java
new file mode 100644
index 0000000..f690469
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Order.java
@@ -0,0 +1,168 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.OrderStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 订单实体
+ * 对应表:orders
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "orders",
+ indexes = {
+ @Index(name = "idx_orders_store_id", columnList = "store_id"),
+ @Index(name = "idx_orders_member_id", columnList = "member_id"),
+ @Index(name = "idx_orders_status", columnList = "status")
+ },
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_orders_order_no", columnNames = "order_no")
+ }
+)
+public class Order extends AbstractBaseEntity {
+ /**
+ * 订单号
+ */
+ @Column(name = "order_no", length = 32)
+ @Comment("订单号")
+ private String orderNo;
+
+
+ /**
+ * 订单总金额
+ */
+ @Column(name = "total_amount", precision = 10, scale = 2)
+ @Comment("订单总金额")
+ private BigDecimal totalAmount;
+
+ /**
+ * 优惠金额
+ */
+ @Column(name = "discount_amount", precision = 10, scale = 2)
+ @Comment("优惠金额")
+ private BigDecimal discountAmount = BigDecimal.ZERO;
+
+ /**
+ * 实付金额
+ */
+ @Column(name = "pay_amount", precision = 10, scale = 2)
+ @Comment("实付金额")
+ private BigDecimal payAmount;
+
+ /**
+ * 使用的券码ID(可为空)
+ */
+ @Column(name = "coupon_code_id")
+ @Comment("优惠券码ID")
+ private Long couponCodeId;
+
+ /**
+ * 订单状态:1-待支付 2-已支付 3-已完成 4-已退款 5-已取消
+ */
+ @Column(name = "status")
+ @Comment("订单状态:1待支付 2已支付 3已完成 4已退款 5已取消")
+ private OrderStatus status;
+
+ /**
+ * 支付时间
+ */
+ @Column(name = "paid_at")
+ @Comment("支付时间")
+ private LocalDateTime paidAt;
+
+
+ // ========== 关联关系 ==========
+
+ /**
+ * 门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "store_id", insertable = false, updatable = false)
+ private Store store;
+
+ /**
+ * 会员
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "member_id", insertable = false, updatable = false)
+ private Member member;
+
+ /**
+ * 优惠券码
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "coupon_code_id", insertable = false, updatable = false)
+ private CouponCode couponCode;
+
+ /**
+ * 订单明细
+ */
+ @OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
+ private List items;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 是否已支付(状态>=已支付且非已取消)
+ */
+ public boolean isPaid() {
+ OrderStatus s = getStatusEnum();
+ if (s == null) return false;
+ return s.getCodeValue() >= OrderStatus.PAID.getCodeValue()
+ && s != OrderStatus.CANCELLED;
+ }
+
+ /**
+ * 是否已完成
+ */
+ public boolean isCompleted() {
+ return getStatusEnum() == OrderStatus.COMPLETED;
+ }
+
+ /**
+ * 是否可退款(已支付或已完成)
+ */
+ public boolean canRefund() {
+ OrderStatus s = getStatusEnum();
+ return s == OrderStatus.PAID || s == OrderStatus.COMPLETED;
+ }
+
+ /**
+ * 是否可取消(待支付)
+ */
+ public boolean canCancel() {
+ return getStatusEnum() == OrderStatus.PENDING;
+ }
+
+ /**
+ * 状态文案
+ */
+ public String getStatusText() {
+ OrderStatus s = getStatusEnum();
+ return s != null ? s.getDesc() : "未知";
+ }
+
+ /**
+ * 是否有优惠
+ */
+ public boolean hasDiscount() {
+ return discountAmount != null && discountAmount.compareTo(BigDecimal.ZERO) > 0;
+ }
+
+ // ========== 枚举转换辅助 ==========
+
+ public OrderStatus getStatusEnum() {
+ return this.status;
+ }
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/OrderItem.java b/src/main/java/com/xjhs/findmemerchant/entity/OrderItem.java
new file mode 100644
index 0000000..640d5f2
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/OrderItem.java
@@ -0,0 +1,123 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.id.SnowflakeGenerated;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 订单明细
+ * 对应表:order_items
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "order_items",
+ indexes = {
+ @Index(name = "idx_order_items_order_id", columnList = "order_id"),
+ @Index(name = "idx_order_items_product_id", columnList = "product_id"),
+ @Index(name = "idx_order_items_sku_id", columnList = "sku_id")
+ }
+)
+public class OrderItem {
+
+ /**
+ * 主键(雪花ID)
+ */
+ @Id
+ @GeneratedValue
+ @SnowflakeGenerated
+ @Comment("主键ID")
+ private Long id;
+
+
+
+ /**
+ * 商品ID(可能被删除所以可空)
+ */
+ @Column(name = "product_id")
+ @Comment("商品ID(可空)")
+ private Long productId;
+
+ /**
+ * 商品名称(快照)
+ */
+ @Column(name = "product_name", length = 100)
+ @Comment("商品名称(快照)")
+ private String productName;
+
+ /**
+ * SKU ID(可能被删除所以可空)
+ */
+ @Column(name = "sku_id")
+ @Comment("SKU ID(可空)")
+ private Long skuId;
+
+ /**
+ * SKU 名称(快照)
+ */
+ @Column(name = "sku_name", length = 100)
+ @Comment("SKU名称(快照)")
+ private String skuName;
+
+ /**
+ * 商品图片(快照)
+ */
+ @Column(name = "image", length = 255)
+ @Comment("商品图片(快照)")
+ private String image;
+
+ /**
+ * 商品单价
+ */
+ @Column(name = "unit_price", precision = 10, scale = 2)
+ @Comment("商品单价")
+ private BigDecimal unitPrice;
+
+ /**
+ * 购买数量
+ */
+ @Column(name = "quantity")
+ @Comment("购买数量")
+ private Integer quantity = 1;
+
+ /**
+ * 小计金额
+ */
+ @Column(name = "total_price", precision = 10, scale = 2)
+ @Comment("小计金额")
+ private BigDecimal totalPrice;
+
+ /**
+ * 创建时间
+ */
+ @Column(name = "created_at")
+ @Comment("创建时间")
+ private LocalDateTime createdAt;
+
+ // ========== 关联关系 ==========
+
+ /**
+ * 所属订单
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "order_id")
+ private Order order;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 根据单价 * 数量计算小计
+ */
+ public BigDecimal getSubtotal() {
+ if (unitPrice == null || quantity == null) {
+ return BigDecimal.ZERO;
+ }
+ return unitPrice.multiply(BigDecimal.valueOf(quantity));
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Product.java b/src/main/java/com/xjhs/findmemerchant/entity/Product.java
new file mode 100644
index 0000000..ca1cd2b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Product.java
@@ -0,0 +1,190 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.ProductStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+
+/**
+ * 商品实体
+ * 对应表:products
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "products",
+ indexes = {
+ @Index(name = "idx_products_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_products_store_id", columnList = "store_id"),
+ @Index(name = "idx_products_category_id", columnList = "category_id"),
+ @Index(name = "idx_products_status", columnList = "status")
+ }
+)
+public class Product extends AbstractBaseEntity {
+
+ /**
+ * 商品名称
+ */
+ @Column(name = "name", length = 100)
+ @Comment("商品名称")
+ private String name;
+
+ /**
+ * 商品描述
+ */
+ @Column(name = "description", columnDefinition = "text")
+ @Comment("商品描述")
+ private String description;
+
+ /**
+ * 商品图片列表(JSON数组字符串)
+ */
+ @Column(name = "images", columnDefinition = "text")
+ @Comment("商品图片(JSON数组)")
+ private String images;
+
+ /**
+ * 原价
+ */
+ @Column(name = "original_price", precision = 10, scale = 2)
+ @Comment("原价")
+ private BigDecimal originalPrice;
+
+ /**
+ * 售价
+ */
+ @Column(name = "sale_price", precision = 10, scale = 2)
+ @Comment("售价")
+ private BigDecimal salePrice;
+
+ /**
+ * 库存,-1 表示无限库存
+ */
+ @Column(name = "stock")
+ @Comment("库存,-1表示无限库存")
+ private Integer stock = -1;
+
+ /**
+ * 已售数量
+ */
+ @Column(name = "sold_count")
+ @Comment("已售数量")
+ private Integer soldCount = 0;
+
+ /**
+ * 排序值
+ */
+ @Column(name = "sort_order")
+ @Comment("排序值")
+ private Integer sortOrder = 0;
+
+ /**
+ * 商品状态:0-下架 1-上架
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(20)",length = 20)
+ @Enumerated(EnumType.STRING)
+ @Comment("商品状态:0下架 1上架")
+ private ProductStatus status = ProductStatus.OFF_SALE;
+
+ // ========== 关联关系 ==========
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ /**
+ * 门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "store_id", insertable = false, updatable = false)
+ private Store store;
+
+ /**
+ * 商品分类
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "category_id", insertable = false, updatable = false)
+ private ProductCategory category;
+
+ /**
+ * SKU 列表
+ */
+ @OneToMany(mappedBy = "product", fetch = FetchType.LAZY)
+ private List skus;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 是否上架中
+ */
+ public boolean isOnSale() {
+ return getStatusEnum() == ProductStatus.ON_SALE;
+ }
+
+ /**
+ * 是否有库存
+ */
+ public boolean hasStock() {
+ if (stock == null) return false;
+ return stock == -1 || stock > 0;
+ }
+
+ /**
+ * 是否有折扣(售价 < 原价)
+ */
+ public boolean hasDiscount() {
+ if (salePrice == null || originalPrice == null) return false;
+ return salePrice.compareTo(originalPrice) < 0;
+ }
+
+ /**
+ * 折扣率 (0-100),整数部分
+ */
+ public int getDiscountRate() {
+ if (originalPrice == null || originalPrice.compareTo(BigDecimal.ZERO) == 0
+ || salePrice == null) {
+ return 0;
+ }
+ BigDecimal rate = salePrice
+ .divide(originalPrice, 2, RoundingMode.HALF_UP)
+ .multiply(BigDecimal.valueOf(100));
+ return rate.intValue();
+ }
+
+ /**
+ * 状态文案
+ */
+ public String getStatusText() {
+ ProductStatus s = getStatusEnum();
+ return s != null ? s.getDesc() : "未知";
+ }
+
+ /**
+ * 是否可扣减库存
+ */
+ public boolean canDeductStock(int quantity) {
+ if (stock == null || quantity <= 0) {
+ return false;
+ }
+ if (stock == -1) {
+ return true; // 无限库存
+ }
+ return stock >= quantity;
+ }
+
+ // ========== 枚举转换辅助 ==========
+
+ public ProductStatus getStatusEnum() {
+ return this.status;
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/ProductCategory.java b/src/main/java/com/xjhs/findmemerchant/entity/ProductCategory.java
new file mode 100644
index 0000000..c0aca82
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/ProductCategory.java
@@ -0,0 +1,109 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.CommonStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.util.List;
+
+/**
+ * 商品分类实体
+ * 对应表:product_categories
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "product_categories",
+ indexes = {
+ @Index(name = "idx_product_categories_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_product_categories_parent_id", columnList = "parent_id"),
+ @Index(name = "idx_product_categories_status", columnList = "status")
+ }
+)
+public class ProductCategory extends AbstractBaseEntity {
+
+ /**
+ * 分类名称
+ */
+ @Column(name = "name", length = 50)
+ @Comment("分类名称")
+ private String name;
+
+ /**
+ * 父分类ID,支持二级分类
+ */
+ @Column(name = "parent_id")
+ @Comment("父分类ID,支持二级分类")
+ private Long parentId;
+
+ /**
+ * 排序值
+ */
+ @Column(name = "sort_order")
+ @Comment("排序值")
+ private Integer sortOrder = 0;
+
+ /**
+ * 状态:0-禁用 1-启用
+ */
+ @Column(name = "status")
+ @Comment("状态:0禁用 1启用")
+ private CommonStatus status = CommonStatus.ENABLED;
+
+ // ========== 关联关系 ==========
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ /**
+ * 父分类
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "parent_id", insertable = false, updatable = false)
+ private ProductCategory parent;
+
+ /**
+ * 子分类列表
+ */
+ @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
+ private List children;
+
+ /**
+ * 分类下商品
+ */
+ @OneToMany(mappedBy = "category", fetch = FetchType.LAZY)
+ private List products;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 是否启用
+ */
+ public boolean isEnabled() {
+ return getStatusEnum() == CommonStatus.ENABLED;
+ }
+
+ /**
+ * 是否为一级分类(无父分类)
+ */
+ public boolean isTopLevel() {
+ return parentId == null;
+ }
+
+ public CommonStatus getStatusEnum() {
+ return this.status;
+ }
+
+ public String getStatusText() {
+ CommonStatus e = getStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/ProductSKU.java b/src/main/java/com/xjhs/findmemerchant/entity/ProductSKU.java
new file mode 100644
index 0000000..aa78e36
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/ProductSKU.java
@@ -0,0 +1,126 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.id.SnowflakeGenerated;
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.CommonStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+
+/**
+ * 商品SKU(规格)实体
+ * 对应表:product_skus
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "product_skus",
+ indexes = {
+ @Index(name = "idx_product_skus_product_id", columnList = "product_id"),
+ @Index(name = "idx_product_skus_sku_code", columnList = "sku_code")
+ }
+)
+public class ProductSKU extends AbstractBaseEntity {
+
+ /**
+ * 主键(雪花ID)
+ */
+ @Id
+ @GeneratedValue
+ @SnowflakeGenerated
+ @Comment("主键ID")
+ private Long id;
+
+ /**
+ * 规格名称(如:大杯/加冰)
+ */
+ @Column(name = "sku_name", length = 100)
+ @Comment("规格名称")
+ private String skuName;
+
+ /**
+ * SKU 编码
+ */
+ @Column(name = "sku_code", length = 50)
+ @Comment("SKU编码")
+ private String skuCode;
+
+ /**
+ * 售价
+ */
+ @Column(name = "price", precision = 10, scale = 2)
+ @Comment("规格价格")
+ private BigDecimal price;
+
+ /**
+ * 库存,-1 表示无限库存
+ */
+ @Column(name = "stock")
+ @Comment("库存,-1表示无限库存")
+ private Integer stock = -1;
+
+ /**
+ * 已售数量
+ */
+ @Column(name = "sold_count")
+ @Comment("已售数量")
+ private Integer soldCount = 0;
+
+ /**
+ * 状态:0-禁用 1-启用
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(20)",length = 20)
+ @Comment("状态:0禁用 1启用")
+ private CommonStatus status = CommonStatus.ENABLED;
+
+
+ /**
+ * 所属商品
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "product_id", insertable = false, updatable = false)
+ private Product product;
+
+ // ===== 业务方法 =====
+
+ /**
+ * 是否启用
+ */
+ public boolean isEnabled() {
+ return getStatusEnum() == CommonStatus.ENABLED;
+ }
+
+ /**
+ * 是否有库存
+ */
+ public boolean hasStock() {
+ if (stock == null) return false;
+ return stock == -1 || stock > 0;
+ }
+
+ /**
+ * 是否可扣减库存
+ */
+ public boolean canDeductStock(int quantity) {
+ if (stock == null || quantity <= 0) {
+ return false;
+ }
+ if (stock == -1) {
+ return true;
+ }
+ return stock >= quantity;
+ }
+
+ public CommonStatus getStatusEnum() {
+ return this.status;
+ }
+
+ public String getStatusText() {
+ CommonStatus e = getStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Review.java b/src/main/java/com/xjhs/findmemerchant/entity/Review.java
new file mode 100644
index 0000000..6928222
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Review.java
@@ -0,0 +1,170 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.ReviewStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+/**
+ * 用户评价(C端共享)
+ * 对应表:reviews
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "reviews",
+ indexes = {
+ @Index(name = "idx_reviews_order_id", columnList = "order_id"),
+ @Index(name = "idx_reviews_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_reviews_store_id", columnList = "store_id"),
+ @Index(name = "idx_reviews_user_id", columnList = "user_id"),
+ @Index(name = "idx_reviews_created_at", columnList = "created_at")
+ }
+)
+public class Review extends AbstractBaseEntity {
+
+
+
+
+ /**
+ * C端用户ID
+ */
+ @Column(name = "user_id")
+ @Comment("C端用户ID")
+ private Long userId;
+
+ /**
+ * 用户昵称
+ */
+ @Column(name = "user_name", length = 50)
+ @Comment("用户昵称")
+ private String userName;
+
+ /**
+ * 用户头像
+ */
+ @Column(name = "user_avatar", length = 255)
+ @Comment("用户头像URL")
+ private String userAvatar;
+
+ /**
+ * 评分 1-5
+ */
+ @Column(name = "rating")
+ @Comment("评分 1-5")
+ private Integer rating;
+
+ /**
+ * 评价内容
+ */
+ @Column(name = "content", columnDefinition = "text")
+ @Comment("评价内容")
+ private String content;
+
+ /**
+ * 图片URL,逗号分隔
+ */
+ @Column(name = "images", length = 1000)
+ @Comment("评价图片URL,逗号分隔")
+ private String images;
+
+ /**
+ * 是否匿名
+ */
+ @Column(name = "is_anonymous")
+ @Comment("是否匿名")
+ private Boolean isAnonymous = Boolean.FALSE;
+
+ /**
+ * 状态:1-正常 0-隐藏
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(20)",length = 20)
+ @Comment("状态:1正常 0隐藏")
+ private ReviewStatus status = ReviewStatus.NORMAL;
+
+ // ========== 关联关系 ==========
+
+ /**
+ * 回复
+ */
+ @OneToOne(mappedBy = "review", fetch = FetchType.LAZY)
+ private ReviewReply reply;
+
+ /**
+ * 门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id")
+ private Merchant merchant;
+ /**
+ * 门店
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "store_id")
+ private Store store;
+
+ /**
+ * 订单
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "order_id")
+ private Order order;
+
+
+
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 评分文案
+ */
+ public String getRatingText() {
+ if (rating == null) {
+ return "未知";
+ }
+ return switch (rating) {
+ case 5 -> "非常满意";
+ case 4 -> "满意";
+ case 3 -> "一般";
+ case 2 -> "不满意";
+ case 1 -> "非常不满意";
+ default -> "未知";
+ };
+ }
+
+ /**
+ * 是否好评(4-5星)
+ */
+ public boolean isPositive() {
+ return rating != null && rating >= 4;
+ }
+
+ /**
+ * 是否差评(1-2星)
+ */
+ public boolean isNegative() {
+ return rating != null && rating <= 2;
+ }
+
+ /**
+ * 展示用昵称(匿名/真实)
+ */
+ public String getDisplayName() {
+ if (Boolean.TRUE.equals(isAnonymous)) {
+ return "匿名用户";
+ }
+ return userName;
+ }
+
+ public ReviewStatus getStatusEnum() {
+ return this.status;
+ }
+
+ public String getStatusText() {
+ ReviewStatus e = getStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/ReviewReply.java b/src/main/java/com/xjhs/findmemerchant/entity/ReviewReply.java
new file mode 100644
index 0000000..3a3b223
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/ReviewReply.java
@@ -0,0 +1,77 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.id.SnowflakeGenerated;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+/**
+ * 商家对评价的回复
+ * 对应表:review_replies
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "review_replies",
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_review_replies_review_id", columnNames = "review_id")
+ },
+ indexes = {
+ @Index(name = "idx_review_replies_merchant_id", columnList = "merchant_id")
+ }
+)
+public class ReviewReply{
+
+ /**
+ * 主键(雪花ID)
+ */
+ @Id
+ @GeneratedValue
+ @SnowflakeGenerated
+ @Comment("主键ID")
+ private Long id;
+
+ /**
+ * 评价ID(唯一,一个评价一条回复)
+ */
+ @Column(name = "review_id")
+ @Comment("评价ID")
+ private Long reviewId;
+
+ /**
+ * 商家ID
+ */
+ @Column(name = "merchant_id")
+ @Comment("商家ID")
+ private Long merchantId;
+
+ /**
+ * 回复内容
+ */
+ @Column(name = "content", columnDefinition = "text")
+ @Comment("回复内容")
+ private String content;
+
+ /**
+ * 回复人ID(商家或员工)
+ */
+ @Column(name = "replier_id")
+ @Comment("回复人ID(商家或员工)")
+ private Long replierId;
+
+ /**
+ * 回复人名称
+ */
+ @Column(name = "replier_name", length = 50)
+ @Comment("回复人名称")
+ private String replierName;
+
+ /**
+ * 所属评价
+ */
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "review_id", insertable = false, updatable = false)
+ private Review review;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Role.java b/src/main/java/com/xjhs/findmemerchant/entity/Role.java
new file mode 100644
index 0000000..e9f9004
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Role.java
@@ -0,0 +1,162 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.json.StringListJsonConverter;
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.xjhs.findmemerchant.constants.RoleConstants.*;
+
+
+/**
+ * 角色实体
+ * 对应表:roles
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "roles",
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_roles_code", columnNames = "code")
+ }
+)
+public class Role extends AbstractBaseEntity {
+
+
+ /**
+ * 角色名称
+ */
+ @Column(name = "name", length = 50)
+ @Comment("角色名称")
+ private String name;
+
+ /**
+ * 角色编码(唯一)
+ */
+ @Column(name = "code", length = 50)
+ @Comment("角色编码")
+ private String code;
+
+ /**
+ * 描述
+ */
+ @Column(name = "description", length = 200)
+ @Comment("角色描述")
+ private String description;
+
+ /**
+ * 权限列表(JSON数组)
+ */
+ @Convert(converter = StringListJsonConverter.class)
+ @Column(name = "permissions", columnDefinition = "json")
+ @Comment("权限列表(JSON数组)")
+ private List permissions = new ArrayList<>();
+
+ /**
+ * 是否系统内置角色:1-是 0-否
+ */
+ @Column(name = "is_system")
+ @Comment("是否系统内置角色:1是 0否")
+ private Byte isSystem = 0;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 是否系统预置角色
+ */
+ public boolean isSystemRole() {
+ return Byte.valueOf((byte) 1).equals(isSystem);
+ }
+
+ /**
+ * 是否拥有指定权限(支持 *)
+ */
+ public boolean hasPermission(String permission) {
+ if (permission == null || permissions == null || permissions.isEmpty()) {
+ return false;
+ }
+ for (String p : permissions) {
+ if (PERMISSION_ALL.equals(p) || permission.equals(p)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 返回权限列表,避免 null
+ */
+ public List getPermissionsSafe() {
+ return permissions != null ? permissions : new ArrayList<>();
+ }
+
+ /**
+ * 系统内置默认角色(不带ID、时间,用于初始化)
+ */
+ public static List getDefaultRoles() {
+ List roles = new ArrayList<>();
+
+ // 店长:全部权限
+ Role owner = new Role();
+ owner.setCode(ROLE_CODE_OWNER);
+ owner.setName("店长");
+ owner.setPermissions(List.of(PERMISSION_ALL));
+ owner.setIsSystem((byte) 1);
+ roles.add(owner);
+
+ // 运营
+ Role operator = new Role();
+ operator.setCode(ROLE_CODE_OPERATOR);
+ operator.setName("运营");
+ operator.setPermissions(List.of(
+ PERMISSION_STORE_VIEW,
+ PERMISSION_COUPON_MANAGE,
+ PERMISSION_COUPON_VIEW,
+ PERMISSION_COUPON_VERIFY,
+ PERMISSION_ORDER_VIEW,
+ PERMISSION_ACTIVITY_MANAGE,
+ PERMISSION_ACTIVITY_VIEW,
+ PERMISSION_ANALYTICS_VIEW,
+ PERMISSION_MEMBER_VIEW
+ ));
+ operator.setIsSystem((byte) 1);
+ roles.add(operator);
+
+ // 客服
+ Role service = new Role();
+ service.setCode(ROLE_CODE_SERVICE);
+ service.setName("客服");
+ service.setPermissions(List.of(
+ PERMISSION_STORE_VIEW,
+ PERMISSION_COUPON_VIEW,
+ PERMISSION_COUPON_VERIFY,
+ PERMISSION_ORDER_VIEW,
+ PERMISSION_MEMBER_VIEW,
+ PERMISSION_MESSAGE_VIEW
+ ));
+ service.setIsSystem((byte) 1);
+ roles.add(service);
+
+ // 营销管理员
+ Role marketing = new Role();
+ marketing.setCode(ROLE_CODE_MARKETING);
+ marketing.setName("营销管理员");
+ marketing.setPermissions(List.of(
+ PERMISSION_COUPON_MANAGE,
+ PERMISSION_COUPON_VIEW,
+ PERMISSION_ACTIVITY_MANAGE,
+ PERMISSION_ACTIVITY_VIEW,
+ PERMISSION_ANALYTICS_VIEW
+ ));
+ marketing.setIsSystem((byte) 1);
+ roles.add(marketing);
+
+ return roles;
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Settlement.java b/src/main/java/com/xjhs/findmemerchant/entity/Settlement.java
new file mode 100644
index 0000000..9e0f89c
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Settlement.java
@@ -0,0 +1,167 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.SettlementStatus;
+import com.xjhs.findmemerchant.types.SettlementType;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 结算记录(按日/按周)
+ * 对应表:settlements
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "settlements",
+ indexes = {
+ @Index(name = "idx_settlements_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_settlements_status", columnList = "status")
+ },
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_settlements_no", columnNames = "settlement_no")
+ }
+)
+public class Settlement extends AbstractBaseEntity {
+
+ private static final DateTimeFormatter PERIOD_DATE_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+
+ /**
+ * 结算单号
+ */
+ @Column(name = "settlement_no", length = 32)
+ @Comment("结算单号")
+ private String settlementNo;
+
+ /**
+ * 结算类型:1-日结 2-周结
+ */
+ @Column(name = "type")
+ @Comment("结算类型:1日结 2周结")
+ private SettlementType type;
+
+ /**
+ * 结算周期开始时间
+ */
+ @Column(name = "period_start")
+ @Comment("结算周期开始时间")
+ private LocalDateTime periodStart;
+
+ /**
+ * 结算周期结束时间
+ */
+ @Column(name = "period_end")
+ @Comment("结算周期结束时间")
+ private LocalDateTime periodEnd;
+
+ /**
+ * 订单数量
+ */
+ @Column(name = "order_count")
+ @Comment("订单数量")
+ private Integer orderCount = 0;
+
+ /**
+ * 订单总额
+ */
+ @Column(name = "total_amount", precision = 12, scale = 2)
+ @Comment("订单总额")
+ private BigDecimal totalAmount;
+
+ /**
+ * 退款金额
+ */
+ @Column(name = "refund_amount", precision = 12, scale = 2)
+ @Comment("退款金额")
+ private BigDecimal refundAmount = BigDecimal.ZERO;
+
+ /**
+ * 平台服务费
+ */
+ @Column(name = "platform_fee", precision = 10, scale = 2)
+ @Comment("平台服务费")
+ private BigDecimal platformFee = BigDecimal.ZERO;
+
+ /**
+ * 结算金额
+ */
+ @Column(name = "settlement_amount", precision = 12, scale = 2)
+ @Comment("结算金额")
+ private BigDecimal settlementAmount;
+
+ /**
+ * 结算状态:0-待结算 1-已结算
+ */
+ @Column(name = "status")
+ @Comment("结算状态:0待结算 1已结算")
+ private SettlementStatus status = SettlementStatus.PENDING;
+
+ /**
+ * 实际结算时间
+ */
+ @Column(name = "settled_at")
+ @Comment("实际结算时间")
+ private LocalDateTime settledAt;
+
+
+
+ // ========== 业务方法 ==========
+
+ public SettlementType getTypeEnum() {
+ return this.type;
+ }
+
+ public SettlementStatus getStatusEnum() {
+ return this.status;
+ }
+ /**
+ * 结算类型文案
+ */
+ public String getTypeText() {
+ SettlementType e = getTypeEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+
+ /**
+ * 状态文案
+ */
+ public String getStatusText() {
+ SettlementStatus e = getStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+
+ /**
+ * 是否已结算
+ */
+ public boolean isSettled() {
+ return getStatusEnum() == SettlementStatus.SETTLED;
+ }
+
+ /**
+ * 周期文案,例如:2024-01-01 ~ 2024-01-07
+ */
+ public String getPeriodText() {
+ if (periodStart == null || periodEnd == null) {
+ return "";
+ }
+ return periodStart.format(PERIOD_DATE_FORMATTER)
+ + " ~ "
+ + periodEnd.format(PERIOD_DATE_FORMATTER);
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Store.java b/src/main/java/com/xjhs/findmemerchant/entity/Store.java
new file mode 100644
index 0000000..550d0d6
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Store.java
@@ -0,0 +1,258 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.BusinessStatus;
+import com.xjhs.findmemerchant.types.CommonStatus;
+import com.xjhs.findmemerchant.types.StoreAuditStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 门店实体
+ * 对应表:stores
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "stores",
+ indexes = {
+ @Index(name = "idx_stores_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_stores_audit_status", columnList = "audit_status")
+ }
+)
+public class Store extends AbstractBaseEntity {
+ /**
+ * 门店名称
+ */
+ @Column(name = "name", length = 100)
+ @Comment("门店名称")
+ private String name;
+
+ /**
+ * 门店Logo
+ */
+ @Column(name = "logo", length = 500)
+ @Comment("门店Logo")
+ private String logo;
+
+ /**
+ * 联系电话
+ */
+ @Column(name = "phone", length = 11)
+ @Comment("联系电话")
+ private String phone;
+
+ /**
+ * 省
+ */
+ @Column(name = "province", length = 50)
+ @Comment("省")
+ private String province;
+
+ /**
+ * 市
+ */
+ @Column(name = "city", length = 50)
+ @Comment("市")
+ private String city;
+
+ /**
+ * 区/县
+ */
+ @Column(name = "district", length = 50)
+ @Comment("区/县")
+ private String district;
+
+ /**
+ * 详细地址
+ */
+ @Column(name = "address", length = 200)
+ @Comment("详细地址")
+ private String address;
+
+ /**
+ * 经度
+ */
+ @Column(name = "longitude", precision = 10, scale = 7)
+ @Comment("经度")
+ private BigDecimal longitude;
+
+ /**
+ * 纬度
+ */
+ @Column(name = "latitude", precision = 10, scale = 7)
+ @Comment("纬度")
+ private BigDecimal latitude;
+
+ /**
+ * 营业时间描述
+ */
+ @Column(name = "business_hours", length = 100)
+ @Comment("营业时间描述")
+ private String businessHours;
+
+ /**
+ * 营业状态:0-已打烊 1-营业中 2-临时打烊
+ */
+ @Column(name = "business_status",columnDefinition = "VARCHAR(20)", length = 20)
+ @Enumerated(EnumType.STRING)
+ @Comment("营业状态:0已打烊 1营业中 2临时打烊")
+ private BusinessStatus businessStatus = BusinessStatus.OPEN;
+
+ /**
+ * 临时打烊原因
+ */
+ @Column(name = "temp_close_reason", length = 200)
+ @Comment("临时打烊原因")
+ private String tempCloseReason;
+
+ /**
+ * 临时打烊结束时间
+ */
+ @Column(name = "temp_close_until")
+ @Comment("临时打烊结束时间")
+ private LocalDateTime tempCloseUntil;
+
+ /**
+ * 审核状态
+ */
+ @Column(name = "audit_status",columnDefinition = "VARCHAR(20)", length = 20)
+ @Comment("审核状态:0待审核 1已通过 2已拒绝")
+ private StoreAuditStatus auditStatus = StoreAuditStatus.PENDING;
+
+ /**
+ * 审核备注
+ */
+ @Column(name = "audit_remark", length = 500)
+ @Comment("审核备注")
+ private String auditRemark;
+
+ /**
+ * 启用状态:0-禁用 1-启用
+ */
+ @Column(name = "status",columnDefinition = "VARCHAR(20)", length = 20)
+ @Comment("启用状态:0禁用 1启用")
+ private CommonStatus status = CommonStatus.ENABLED;
+
+ // ========== 关联关系 ==========
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id")
+ private Merchant merchant;
+
+ /**
+ * 员工列表
+ */
+ @OneToMany(mappedBy = "store", fetch = FetchType.LAZY,cascade = CascadeType.ALL,orphanRemoval = true)
+ private List employees = new ArrayList<>();
+
+ public void addEmployee(Employee employee){
+ employee.setStore(this);
+ this.employees.add(employee);
+ }
+
+ /**
+ * 营业时间段
+ */
+ @OneToMany(mappedBy = "store", fetch = FetchType.LAZY,cascade = CascadeType.ALL,orphanRemoval = true)
+ private List businessPeriods = new ArrayList<>();
+
+ public void addBusinessPeriods(BusinessPeriod businessPeriod){
+ businessPeriod.setStore(this);
+ this.businessPeriods.add(businessPeriod);
+ }
+
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 完整地址
+ */
+ public String getFullAddress() {
+ return (province == null ? "" : province)
+ + (city == null ? "" : city)
+ + (district == null ? "" : district)
+ + (address == null ? "" : address);
+ }
+
+ /**
+ * 是否审核通过
+ */
+ public boolean isApproved() {
+ return getAuditStatusEnum() == StoreAuditStatus.APPROVED;
+ }
+
+ /**
+ * 是否启用
+ */
+ public boolean isActive() {
+ return getStatusEnum() == CommonStatus.ENABLED;
+ }
+
+ /**
+ * 是否有地理位置
+ */
+ public boolean hasLocation() {
+ return longitude != null && latitude != null;
+ }
+
+ /**
+ * 当前是否营业
+ */
+ public boolean isBusinessOpen() {
+ BusinessStatus bs = getBusinessStatusEnum();
+ if (bs == null) {
+ return false;
+ }
+ if (bs == BusinessStatus.TEMP_CLOSED) {
+ // 如果设置了临时打烊结束时间且已过期,则视作应恢复营业
+ if (tempCloseUntil != null && LocalDateTime.now().isAfter(tempCloseUntil)) {
+ return true;
+ }
+ return false;
+ }
+ return bs == BusinessStatus.OPEN;
+ }
+
+ /**
+ * 营业状态文案
+ */
+ public String getBusinessStatusText() {
+ BusinessStatus bs = getBusinessStatusEnum();
+ return bs != null ? bs.getDesc() : "未知";
+ }
+
+ public BusinessStatus getBusinessStatusEnum() {
+ return this.businessStatus;
+ }
+
+
+ public StoreAuditStatus getAuditStatusEnum() {
+ return this.auditStatus;
+ }
+
+ public CommonStatus getStatusEnum() {
+ return this.status;
+ }
+
+ public String getAuditStatusText() {
+ StoreAuditStatus e = getAuditStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+
+ public String getStatusText() {
+ CommonStatus e = getStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Transaction.java b/src/main/java/com/xjhs/findmemerchant/entity/Transaction.java
new file mode 100644
index 0000000..4a1c803
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Transaction.java
@@ -0,0 +1,133 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.id.SnowflakeGenerated;
+import com.xjhs.findmemerchant.types.TransactionType;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 钱包交易流水
+ * 对应表:wallet_transactions
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "wallet_transactions",
+ indexes = {
+ @Index(name = "idx_wallet_transactions_wallet_id", columnList = "wallet_id"),
+ @Index(name = "idx_wallet_transactions_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_wallet_transactions_type", columnList = "type"),
+ @Index(name = "idx_wallet_transactions_created_at", columnList = "created_at")
+ }
+)
+public class Transaction {
+
+ /**
+ * 主键(雪花ID)
+ */
+ @Id
+ @GeneratedValue
+ @SnowflakeGenerated
+ @Comment("主键ID")
+ private Long id;
+
+
+ /**
+ * 交易类型:1-收入 2-支出 3-冻结 4-解冻 5-提现
+ */
+ @Column(name = "type",columnDefinition = "VARCHAR(20)",length = 20)
+ @Comment("交易类型:1收入 2支出 3冻结 4解冻 5提现")
+ private TransactionType type;
+
+ /**
+ * 交易金额
+ */
+ @Column(name = "amount", precision = 12, scale = 2)
+ @Comment("交易金额")
+ private BigDecimal amount;
+
+ /**
+ * 交易前余额
+ */
+ @Column(name = "balance_before", precision = 12, scale = 2)
+ @Comment("交易前余额")
+ private BigDecimal balanceBefore;
+
+ /**
+ * 交易后余额
+ */
+ @Column(name = "balance_after", precision = 12, scale = 2)
+ @Comment("交易后余额")
+ private BigDecimal balanceAfter;
+
+ /**
+ * 关联类型: order, withdrawal, settlement
+ */
+ @Column(name = "ref_type", length = 32)
+ @Comment("关联类型:order/withdrawal/settlement等")
+ private String refType;
+
+ /**
+ * 关联ID
+ */
+ @Column(name = "ref_id")
+ @Comment("关联业务ID")
+ private Long refId;
+
+ /**
+ * 描述
+ */
+ @Column(name = "description", length = 200)
+ @Comment("交易描述")
+ private String description;
+
+ /**
+ * 创建时间
+ */
+ @Column(name = "created_at")
+ @Comment("创建时间")
+ private LocalDateTime createdAt;
+
+ /**
+ * 所属钱包
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "wallet_id", insertable = false, updatable = false)
+ private Wallet wallet;
+
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+
+ // ========== 业务方法 ==========
+
+ public TransactionType getTypeEnum() {
+ return this.type;
+ }
+
+ /**
+ * 交易类型文案
+ */
+ public String getTypeText() {
+ TransactionType t = getTypeEnum();
+ return t != null ? t.getDesc() : "未知";
+ }
+
+ /**
+ * 是否收入流水
+ */
+ public boolean isIncome() {
+ return getTypeEnum() == TransactionType.INCOME;
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Wallet.java b/src/main/java/com/xjhs/findmemerchant/entity/Wallet.java
new file mode 100644
index 0000000..06dffe2
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Wallet.java
@@ -0,0 +1,100 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+
+/**
+ * 商家钱包
+ * 对应表:wallets
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "wallets",
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_wallets_merchant_id", columnNames = "merchant_id")
+ }
+)
+public class Wallet extends AbstractBaseEntity {
+
+ /**
+ * 可用余额
+ */
+ @Column(name = "balance", precision = 12, scale = 2)
+ @Comment("可用余额")
+ private BigDecimal balance = BigDecimal.ZERO;
+
+ /**
+ * 冻结余额(提现中)
+ */
+ @Column(name = "frozen_balance", precision = 12, scale = 2)
+ @Comment("冻结余额(提现中)")
+ private BigDecimal frozenBalance = BigDecimal.ZERO;
+
+ /**
+ * 累计收入
+ */
+ @Column(name = "total_income", precision = 12, scale = 2)
+ @Comment("累计收入")
+ private BigDecimal totalIncome = BigDecimal.ZERO;
+
+ /**
+ * 累计提现
+ */
+ @Column(name = "total_withdrawn", precision = 12, scale = 2)
+ @Comment("累计提现")
+ private BigDecimal totalWithdrawn = BigDecimal.ZERO;
+
+ /**
+ * 待结算金额
+ */
+ @Column(name = "pending_settlement", precision = 12, scale = 2)
+ @Comment("待结算金额")
+ private BigDecimal pendingSettlement = BigDecimal.ZERO;
+
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id")
+ private Merchant merchant;
+
+ // ========== 业务方法 ==========
+
+ /**
+ * 可提现余额
+ */
+ public BigDecimal getAvailableBalance() {
+ return balance != null ? balance : BigDecimal.ZERO;
+ }
+
+ /**
+ * 总余额(含冻结)
+ */
+ public BigDecimal getTotalBalance() {
+ BigDecimal b = balance != null ? balance : BigDecimal.ZERO;
+ BigDecimal f = frozenBalance != null ? frozenBalance : BigDecimal.ZERO;
+ return b.add(f);
+ }
+
+ /**
+ * 是否可提现指定金额
+ */
+ public boolean canWithdraw(BigDecimal amount) {
+ if (amount == null) {
+ return false;
+ }
+ if (amount.compareTo(BigDecimal.ZERO) <= 0) {
+ return false;
+ }
+ BigDecimal b = balance != null ? balance : BigDecimal.ZERO;
+ return b.compareTo(amount) >= 0;
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/entity/Withdrawal.java b/src/main/java/com/xjhs/findmemerchant/entity/Withdrawal.java
new file mode 100644
index 0000000..8efa02e
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/entity/Withdrawal.java
@@ -0,0 +1,175 @@
+package com.xjhs.findmemerchant.entity;
+
+import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
+import com.xjhs.findmemerchant.types.WithdrawalStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.Comment;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 提现申请
+ * 对应表:withdrawals
+ */
+@Getter
+@Setter
+@Entity
+@Table(
+ name = "withdrawals",
+ indexes = {
+ @Index(name = "idx_withdrawals_merchant_id", columnList = "merchant_id"),
+ @Index(name = "idx_withdrawals_wallet_id", columnList = "wallet_id"),
+ @Index(name = "idx_withdrawals_status", columnList = "status"),
+ @Index(name = "idx_withdrawals_created_at", columnList = "created_at")
+ }
+)
+public class Withdrawal extends AbstractBaseEntity {
+
+
+ /**
+ * 提现金额
+ */
+ @Column(name = "amount", precision = 12, scale = 2)
+ @Comment("提现金额")
+ private BigDecimal amount;
+
+ /**
+ * 手续费
+ */
+ @Column(name = "fee", precision = 10, scale = 2)
+ @Comment("提现手续费")
+ private BigDecimal fee = BigDecimal.ZERO;
+
+ /**
+ * 实际到账金额
+ */
+ @Column(name = "actual_amount", precision = 12, scale = 2)
+ @Comment("实际到账金额")
+ private BigDecimal actualAmount;
+
+ /**
+ * 银行名称
+ */
+ @Column(name = "bank_name", length = 50)
+ @Comment("银行名称")
+ private String bankName;
+
+ /**
+ * 银行账号(加密存储)
+ */
+ @Column(name = "bank_account", length = 30)
+ @Comment("银行账号(加密)")
+ private String bankAccount;
+
+ /**
+ * 户名
+ */
+ @Column(name = "account_name", length = 50)
+ @Comment("开户人姓名")
+ private String accountName;
+
+ /**
+ * 状态:0-待审核 1-处理中 2-已完成 3-已拒绝 4-已取消
+ */
+ @Column(name = "status")
+ @Comment("提现状态")
+ private Byte status = 0;
+
+ /**
+ * 拒绝原因
+ */
+ @Column(name = "reject_reason", length = 200)
+ @Comment("拒绝原因")
+ private String rejectReason;
+
+ /**
+ * 审核/处理时间
+ */
+ @Column(name = "processed_at")
+ @Comment("审核/处理时间")
+ private LocalDateTime processedAt;
+
+ /**
+ * 完成时间
+ */
+ @Column(name = "completed_at")
+ @Comment("完成时间")
+ private LocalDateTime completedAt;
+
+ /**
+ * 银行流水号
+ */
+ @Column(name = "transaction_no", length = 64)
+ @Comment("银行流水号")
+ private String transactionNo;
+
+ /**
+ * 备注
+ */
+ @Column(name = "remark", length = 200)
+ @Comment("备注")
+ private String remark;
+
+ /**
+ * 商家
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "merchant_id", insertable = false, updatable = false)
+ private Merchant merchant;
+
+ /**
+ * 钱包
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "wallet_id", insertable = false, updatable = false)
+ private Wallet wallet;
+
+ // ========= 业务方法 =========
+
+ public WithdrawalStatus getStatusEnum() {
+ return WithdrawalStatus.fromCode(this.status);
+ }
+
+ public void setStatusEnum(WithdrawalStatus statusEnum) {
+ this.status = statusEnum == null ? null : statusEnum.code();
+ }
+
+ public String getStatusText() {
+ WithdrawalStatus e = getStatusEnum();
+ return e != null ? e.getDesc() : "未知";
+ }
+
+ /**
+ * 是否待审核
+ */
+ public boolean isPending() {
+ return getStatusEnum() == WithdrawalStatus.PENDING;
+ }
+
+ /**
+ * 是否已完成
+ */
+ public boolean isCompleted() {
+ return getStatusEnum() == WithdrawalStatus.COMPLETED;
+ }
+
+ /**
+ * 是否可以取消
+ */
+ public boolean canCancel() {
+ return getStatusEnum() == WithdrawalStatus.PENDING;
+ }
+
+ /**
+ * 脱敏银行卡号
+ */
+ public String getMaskedBankAccount() {
+ if (bankAccount == null || bankAccount.length() <= 8) {
+ return bankAccount;
+ }
+ return bankAccount.substring(0, 4) + "****" + bankAccount.substring(bankAccount.length() - 4);
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/file/FileController.java b/src/main/java/com/xjhs/findmemerchant/file/FileController.java
deleted file mode 100644
index 15eb8ab..0000000
--- a/src/main/java/com/xjhs/findmemerchant/file/FileController.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.xjhs.findmemerchant.file;
-
-import com.xjhs.findmemerchant.common.ApiResult;
-import com.xjhs.findmemerchant.file.dao.FileMetaInfo;
-import com.xjhs.findmemerchant.file.dao.FileMetaInfoRepository;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.tika.mime.MimeTypes;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.UUID;
-
-/**
- * 统一文件管理控制器
- */
-@Slf4j
-@RestController
-@RequiredArgsConstructor
-public class FileController {
-
-
- @Value("${appconfig.saveFileRoot}")
- private String fileSaveRoot="./file-data";
-
- private final FileMetaInfoRepository fileMetaInfoRepository;
-
- /**
- * 获取并检查创建文件存储目录
- * @return 文件存储目录带日期
- */
- public Path getFileSavePath() throws IOException {
- var datePath = DateTimeFormatter.ofPattern("yyyy/MM-dd").format(LocalDate.now());
- var path = Path.of(this.fileSaveRoot, datePath);
- if (!Files.exists(path)){
- Files.createDirectories(path);
- }
- return path;
- }
-
-
- /**
- * 上传文件
- *
- * @param file 文件数据内容
- * @return 文件描信息
- */
-
- @PostMapping("/platform/file/upload")
- public ApiResult uploadFile(@RequestPart(name = "file") MultipartFile file) {
- try {
- var ext = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
- if (StringUtils.isEmpty(ext)) {
- try {
- ext = MimeTypes.getDefaultMimeTypes().forName(file.getContentType()).getExtension();
- } catch (Exception e) {
- ext = "";
- }
- }
-
- var filePath = Path.of(this.fileSaveRoot, UUID.randomUUID() + ext);
- file.transferTo(filePath);
- var fileMetaInfo = new FileMetaInfo();
- fileMetaInfo.setName(file.getOriginalFilename());
- fileMetaInfo.setContentType(file.getContentType());
- fileMetaInfo.setExtension(ext);
- fileMetaInfo.setSavePath(filePath.toFile().getAbsolutePath());
- fileMetaInfoRepository.save(fileMetaInfo);
- return ApiResult.data(fileMetaInfo);
- } catch (Exception e) {
- log.error(e.getMessage(),e);
- return ApiResult.fail(e.getMessage());
- }
- }
-
- /**
- * 预览文件
- * @param fileId 文件id
- * @return 文件内容
- */
- @GetMapping("/platform/file/view/{fileId}")
- public ResponseEntity> uploadFile(@PathVariable("fileId") String fileId) {
- return this.fileMetaInfoRepository.findById(fileId).map(item->{
- try {
- var bytes = Files.readAllBytes(Paths.get(item.getSavePath()));
- return ResponseEntity.ok()
- .contentType(MediaType.parseMediaType(item.getContentType()))
- .body(bytes);
- } catch (IOException e) {
- return ResponseEntity.internalServerError().build();
- }
-
- }).orElse(ResponseEntity.internalServerError().build());
-
- }
-
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/xjhs/findmemerchant/file/dao/FileMetaInfo.java b/src/main/java/com/xjhs/findmemerchant/file/dao/FileMetaInfo.java
deleted file mode 100644
index d735ee9..0000000
--- a/src/main/java/com/xjhs/findmemerchant/file/dao/FileMetaInfo.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.xjhs.findmemerchant.file.dao;
-
-import com.xjhs.findmemerchant.common.jpa.id.SnowflakeGenerated;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Id;
-import lombok.Data;
-import org.hibernate.annotations.Comment;
-
-import java.time.LocalDateTime;
-
-
-/**
- * 文件信息表
- */
-@Data
-@Entity
-@Comment("文件信息表")
-public class FileMetaInfo {
- @Id
- @SnowflakeGenerated
- @Comment("主键")
- private Long id;
- @Comment("文件原始名称")
- private String name;
- @Comment("文件媒体类型")
- private String contentType;
- @Comment("文件扩展名称")
- @Column(length = 10)
- private String extension;
- @Comment("本地存储路径")
- private String savePath;
- @Comment("是否已迁移到云服务")
- private boolean moveToCloud = false;
- @Comment("云对象id")
- @Column(length = 50)
- private String cloudObjectId;
- @Comment("创建时间")
- private LocalDateTime createTime = LocalDateTime.now();
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/xjhs/findmemerchant/file/dao/FileMetaInfoRepository.java b/src/main/java/com/xjhs/findmemerchant/file/dao/FileMetaInfoRepository.java
deleted file mode 100644
index a7f845a..0000000
--- a/src/main/java/com/xjhs/findmemerchant/file/dao/FileMetaInfoRepository.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.xjhs.findmemerchant.file.dao;
-
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.stereotype.Repository;
-
-@Repository
-public interface FileMetaInfoRepository extends JpaRepository, JpaSpecificationExecutor {
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/xjhs/findmemerchant/mapper/EmployeeMapper.java b/src/main/java/com/xjhs/findmemerchant/mapper/EmployeeMapper.java
new file mode 100644
index 0000000..5ec5ed3
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/mapper/EmployeeMapper.java
@@ -0,0 +1,20 @@
+package com.xjhs.findmemerchant.mapper;
+
+import com.xjhs.findmemerchant.dto.member.EmployeeDto;
+import com.xjhs.findmemerchant.entity.Employee;
+import com.xjhs.findmemerchant.vo.member.EmployeeCreateVo;
+import com.xjhs.findmemerchant.vo.member.EmployeeUpdateVo;
+import org.mapstruct.*;
+
+@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE,unmappedSourcePolicy = ReportingPolicy.IGNORE)
+public interface EmployeeMapper {
+ @Mapping(target = "storeId",source = "store.id")
+ @Mapping(target = "roleId",source = "role.id")
+ EmployeeDto toDto(Employee employee);
+
+ Employee toEntity(EmployeeCreateVo createVo);
+
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void updateEntityFormUpdateVo(EmployeeUpdateVo updateVo, @MappingTarget Employee employee);
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/mapper/MerchantMapper.java b/src/main/java/com/xjhs/findmemerchant/mapper/MerchantMapper.java
index 95c0db5..e74e846 100644
--- a/src/main/java/com/xjhs/findmemerchant/mapper/MerchantMapper.java
+++ b/src/main/java/com/xjhs/findmemerchant/mapper/MerchantMapper.java
@@ -1,12 +1,15 @@
package com.xjhs.findmemerchant.mapper;
-import com.xjhs.findmemerchant.dto.merchant.MerchantDto;
+import com.xjhs.findmemerchant.dto.MerchantDto;
import com.xjhs.findmemerchant.entity.Merchant;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE,unmappedSourcePolicy = ReportingPolicy.IGNORE)
public interface MerchantMapper {
+
+ @Mapping(target = "authStatusDesc",source = "authStatus.desc")
MerchantDto toDto(Merchant merchant);
}
diff --git a/src/main/java/com/xjhs/findmemerchant/mapper/StoreMapper.java b/src/main/java/com/xjhs/findmemerchant/mapper/StoreMapper.java
new file mode 100644
index 0000000..0db647e
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/mapper/StoreMapper.java
@@ -0,0 +1,32 @@
+package com.xjhs.findmemerchant.mapper;
+
+import com.xjhs.findmemerchant.dto.store.BusinessPeriodDto;
+import com.xjhs.findmemerchant.dto.store.StoreBusinessStatusDto;
+import com.xjhs.findmemerchant.dto.store.StoreDto;
+import com.xjhs.findmemerchant.entity.BusinessPeriod;
+import com.xjhs.findmemerchant.entity.Store;
+import com.xjhs.findmemerchant.vo.store.BusinessPeriodVo;
+import com.xjhs.findmemerchant.vo.store.StoreBusinessStatusUpdateVo;
+import com.xjhs.findmemerchant.vo.store.StoreCreateVo;
+import com.xjhs.findmemerchant.vo.store.StoreUpdateVo;
+import org.mapstruct.*;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE,unmappedSourcePolicy = ReportingPolicy.IGNORE)
+public interface StoreMapper {
+ StoreDto toDto(Store store);
+ StoreBusinessStatusDto toBusinessStatusDto(Store store);
+ Store toEntity(StoreCreateVo createVo);
+ Store toEntity(StoreUpdateVo updateVo);
+
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void updateFromVo(StoreUpdateVo vo, @MappingTarget Store entity);
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void updateFromBusinessUpdateVo(StoreBusinessStatusUpdateVo updateVo,@MappingTarget Store store);
+
+ BusinessPeriod toEntity(BusinessPeriodVo vo);
+ @Mapping(source = "store.id",target = "storeId")
+ BusinessPeriodDto toDto(BusinessPeriod entity);
+ List toDtoList(List entityList);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/mapper/SystemUserMapper.java b/src/main/java/com/xjhs/findmemerchant/mapper/SystemUserMapper.java
deleted file mode 100644
index cca0ad0..0000000
--- a/src/main/java/com/xjhs/findmemerchant/mapper/SystemUserMapper.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.xjhs.findmemerchant.mapper;
-
-import com.xjhs.findmemerchant.security.LoginUser;
-import com.xjhs.findmemerchant.system.entity.SystemUser;
-import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.mapstruct.ReportingPolicy;
-
-
-@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE,unmappedSourcePolicy = ReportingPolicy.IGNORE)
-public interface SystemUserMapper {
-
- @Mapping(target = "merchantId",source = "systemUser.merchant.id")
- LoginUser toLoginUserInfo(SystemUser systemUser);
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/ActivityRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/ActivityRepository.java
new file mode 100644
index 0000000..fd53c89
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/ActivityRepository.java
@@ -0,0 +1,14 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Activity;
+import com.xjhs.findmemerchant.types.ActivityStatus;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface ActivityRepository extends JpaRepository {
+
+ List findByMerchant_Id(Long merchantId);
+
+ List findByStatus(ActivityStatus status);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/BankCardRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/BankCardRepository.java
new file mode 100644
index 0000000..7c2df2b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/BankCardRepository.java
@@ -0,0 +1,25 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.BankCard;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 商家银行卡仓储
+ */
+public interface BankCardRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按商家查询所有银行卡
+ */
+ List findByMerchant_Id(Long merchantId);
+
+ /**
+ * 查询商家的默认卡
+ */
+ Optional findByMerchant_IdAndIsDefaultTrue(Long merchantId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/BusinessLicenseRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/BusinessLicenseRepository.java
new file mode 100644
index 0000000..5dcf65a
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/BusinessLicenseRepository.java
@@ -0,0 +1,16 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.BusinessLicense;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface BusinessLicenseRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ List findByMerchant_Id(Long merchantId);
+
+ Optional findTopByMerchant_IdOrderByCreatedAtDesc(Long merchantId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/BusinessPeriodRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/BusinessPeriodRepository.java
new file mode 100644
index 0000000..0980b1b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/BusinessPeriodRepository.java
@@ -0,0 +1,24 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.BusinessPeriod;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 门店营业时间段仓储
+ */
+public interface BusinessPeriodRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按门店查询所有营业时间段
+ */
+ List findByStore_Id(Long storeId);
+
+ /**
+ * 按门店 + 周几查询启用的时间段
+ */
+ List findByStore_IdAndDayOfWeekAndIsEnabledTrue(Long storeId, Byte dayOfWeek);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/CouponCodeRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/CouponCodeRepository.java
new file mode 100644
index 0000000..2d21ff2
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/CouponCodeRepository.java
@@ -0,0 +1,30 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.CouponCode;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 优惠券码仓储
+ */
+public interface CouponCodeRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按券模板查询券码
+ */
+ List findByCoupon_Id(Long couponId);
+
+ /**
+ * 按会员查询券码
+ */
+ List findByMember_Id(Long memberId);
+
+ /**
+ * 按券码查询
+ */
+ Optional findByCode(String code);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/CouponRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/CouponRepository.java
new file mode 100644
index 0000000..6638486
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/CouponRepository.java
@@ -0,0 +1,29 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Coupon;
+import com.xjhs.findmemerchant.types.CouponStatus;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 优惠券模板仓储
+ */
+public interface CouponRepository extends JpaRepository {
+
+ /**
+ * 按商家查询优惠券
+ */
+ List findByMerchant_Id(Long merchantId);
+
+ /**
+ * 按状态查询优惠券
+ */
+ List findByStatus(CouponStatus status);
+
+ /**
+ * 按ID + 商家ID 查询(防越权)
+ */
+ Optional findByIdAndMerchant_Id(Long id, Long merchantId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/CouponStoreRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/CouponStoreRepository.java
new file mode 100644
index 0000000..8e762d8
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/CouponStoreRepository.java
@@ -0,0 +1,20 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.CouponStore;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 优惠券与门店关联仓储
+ */
+public interface CouponStoreRepository extends JpaSpecificationExecutor,JpaRepository {
+
+ List findByCoupon_Id(Long couponId);
+
+ List findByStore_Id(Long storeId);
+
+ Optional findByCoupon_IdAndStore_Id(Long couponId, Long storeId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/EmployeeRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/EmployeeRepository.java
new file mode 100644
index 0000000..042e926
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/EmployeeRepository.java
@@ -0,0 +1,17 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Employee;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 员工仓储
+ */
+public interface EmployeeRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ List findByStoreId(Long storeId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/HealthCertificateRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/HealthCertificateRepository.java
new file mode 100644
index 0000000..99ba8e9
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/HealthCertificateRepository.java
@@ -0,0 +1,15 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.HealthCertificate;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+public interface HealthCertificateRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ List findByStore_Id(Long storeId);
+
+ List findByEmployee_Id(Long employeeId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/MemberRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/MemberRepository.java
new file mode 100644
index 0000000..87edaf7
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/MemberRepository.java
@@ -0,0 +1,27 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Member;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 会员仓储
+ */
+public interface MemberRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按商家查询会员
+ */
+ List findByMerchant_Id(Long merchantId);
+
+ /**
+ * 按商家 + 手机号查询(唯一)
+ */
+ Optional findByPhone(String phone);
+
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/MerchantRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/MerchantRepository.java
index ae7944c..0a51aaa 100644
--- a/src/main/java/com/xjhs/findmemerchant/repository/MerchantRepository.java
+++ b/src/main/java/com/xjhs/findmemerchant/repository/MerchantRepository.java
@@ -6,6 +6,16 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.Optional;
-public interface MerchantRepository extends JpaSpecificationExecutor, JpaRepository {
- Optional findBySystemUserPhone(String phone);
+/**
+ * 商家仓储
+ */
+public interface MerchantRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 根据手机号查询
+ */
+ Optional findByPhone(String phone);
+
+ Boolean existsByPhone(String phone);
}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/MessageRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/MessageRepository.java
new file mode 100644
index 0000000..79989a0
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/MessageRepository.java
@@ -0,0 +1,26 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Message;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 消息仓储
+ */
+public interface MessageRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按商家查询消息
+ */
+ List findByMerchant_Id(Long merchantId);
+
+ List findByMerchant_IdAndIsRead(Long merchantId, Boolean isRead);
+
+ /**
+ * 统计未读消息数量
+ */
+ long countByMerchant_IdAndIsRead(Long merchantId, Boolean isRead);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/OrderItemRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/OrderItemRepository.java
new file mode 100644
index 0000000..138e3c9
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/OrderItemRepository.java
@@ -0,0 +1,21 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.OrderItem;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 订单明细仓储
+ */
+public interface OrderItemRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按订单查询订单明细
+ */
+ List findByOrder_Id(Long orderId);
+
+ List findByProductId(Long productId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/OrderRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/OrderRepository.java
new file mode 100644
index 0000000..f32769e
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/OrderRepository.java
@@ -0,0 +1,27 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Order;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 订单仓储
+ */
+public interface OrderRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按订单号查询(唯一)
+ */
+ Optional findByOrderNo(String orderNo);
+
+ /**
+ * 按门店查询订单
+ */
+ List findByStore_Id(Long storeId);
+
+ List findByMember_Id(Long memberId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/ProductCategoryRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/ProductCategoryRepository.java
new file mode 100644
index 0000000..59f6448
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/ProductCategoryRepository.java
@@ -0,0 +1,24 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.ProductCategory;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 商品分类仓储
+ */
+public interface ProductCategoryRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按商家查询所有分类
+ */
+ List findByMerchant_Id(Long merchantId);
+
+ /**
+ * 查询某商家下指定父分类的子分类
+ */
+ List findByMerchantIdAndParentId(Long merchantId, Long parentId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/ProductRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/ProductRepository.java
new file mode 100644
index 0000000..77a861f
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/ProductRepository.java
@@ -0,0 +1,32 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Product;
+import com.xjhs.findmemerchant.types.ProductStatus;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 商品仓储
+ */
+public interface ProductRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ List findByMerchant_Id(Long merchantId);
+
+ /**
+ * 按门店查询商品
+ */
+ List findByStoreId(Long storeId);
+
+ /**
+ * 按分类查询商品
+ */
+ List findByCategoryId(Long categoryId);
+
+ /**
+ * 按状态查询商品
+ */
+ List findByStatus(ProductStatus status);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/ProductSKURepository.java b/src/main/java/com/xjhs/findmemerchant/repository/ProductSKURepository.java
new file mode 100644
index 0000000..71f2a8d
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/ProductSKURepository.java
@@ -0,0 +1,24 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.ProductSKU;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 商品SKU仓储
+ */
+public interface ProductSKURepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 查询商品下的所有SKU
+ */
+ List findByProductId(Long productId);
+
+ /**
+ * 按编码查询
+ */
+ List findBySkuCode(String skuCode);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/ReviewReplyRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/ReviewReplyRepository.java
new file mode 100644
index 0000000..42a1fc7
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/ReviewReplyRepository.java
@@ -0,0 +1,16 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.ReviewReply;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.Optional;
+
+/**
+ * 评价回复仓储
+ */
+public interface ReviewReplyRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ Optional findByReviewId(Long reviewId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/ReviewRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/ReviewRepository.java
new file mode 100644
index 0000000..79a70cd
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/ReviewRepository.java
@@ -0,0 +1,22 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Review;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 用户评价仓储
+ */
+public interface ReviewRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ List findByMerchant_Id(Long merchantId);
+
+ List findByStoreId(Long storeId);
+
+ List findByOrderId(Long orderId);
+
+ List findByUserId(Long userId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/RoleRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/RoleRepository.java
new file mode 100644
index 0000000..7971f97
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/RoleRepository.java
@@ -0,0 +1,19 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Role;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.Optional;
+
+/**
+ * 角色仓储
+ */
+public interface RoleRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按编码查询角色
+ */
+ Optional findByCode(String code);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/SettlementRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/SettlementRepository.java
new file mode 100644
index 0000000..275b644
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/SettlementRepository.java
@@ -0,0 +1,25 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Settlement;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 结算记录仓储
+ */
+public interface SettlementRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按结算单号查询
+ */
+ Optional findBySettlementNo(String settlementNo);
+
+ /**
+ * 按商家查询结算记录
+ */
+ List findByMerchantId(Long merchantId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/StoreRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/StoreRepository.java
new file mode 100644
index 0000000..2ee48b0
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/StoreRepository.java
@@ -0,0 +1,22 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Store;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 门店仓储
+ */
+public interface StoreRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按商家查询门店
+ */
+ List findByMerchantId(Long merchantId);
+
+ Optional findByMerchant_IdAndId(Long merchantId, Long id);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/TransactionRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/TransactionRepository.java
new file mode 100644
index 0000000..ef1d01b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/TransactionRepository.java
@@ -0,0 +1,24 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Transaction;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 钱包交易流水仓储
+ */
+public interface TransactionRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按钱包查询流水
+ */
+ List findByWalletId(Long walletId);
+
+ /**
+ * 按商家查询流水
+ */
+ List findByMerchantId(Long merchantId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/WalletRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/WalletRepository.java
new file mode 100644
index 0000000..515f6b1
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/WalletRepository.java
@@ -0,0 +1,19 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Wallet;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.Optional;
+
+/**
+ * 商家钱包仓储
+ */
+public interface WalletRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 根据商家ID查询钱包(唯一)
+ */
+ Optional findByMerchantId(Long merchantId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/repository/WithdrawalRepository.java b/src/main/java/com/xjhs/findmemerchant/repository/WithdrawalRepository.java
new file mode 100644
index 0000000..0754d57
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/repository/WithdrawalRepository.java
@@ -0,0 +1,24 @@
+package com.xjhs.findmemerchant.repository;
+
+import com.xjhs.findmemerchant.entity.Withdrawal;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * 提现仓储
+ */
+public interface WithdrawalRepository extends JpaRepository,
+ JpaSpecificationExecutor {
+
+ /**
+ * 按商家查提现记录
+ */
+ List findByMerchantId(Long merchantId);
+
+ /**
+ * 按钱包查提现记录
+ */
+ List findByWalletId(Long walletId);
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/security/JwtAuthenticationFilter.java b/src/main/java/com/xjhs/findmemerchant/security/JwtAuthenticationFilter.java
index bb23f89..fd97ad4 100644
--- a/src/main/java/com/xjhs/findmemerchant/security/JwtAuthenticationFilter.java
+++ b/src/main/java/com/xjhs/findmemerchant/security/JwtAuthenticationFilter.java
@@ -1,7 +1,7 @@
package com.xjhs.findmemerchant.security;
-import com.xjhs.findmemerchant.mapper.SystemUserMapper;
+import com.xjhs.findmemerchant.repository.MemberRepository;
import com.xjhs.findmemerchant.repository.MerchantRepository;
import com.xjhs.findmemerchant.security.sms.SmsAuthenticationToken;
import jakarta.servlet.FilterChain;
@@ -24,18 +24,24 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenService jwtTokenService;
private final MerchantRepository merchantRepository;
- private final SystemUserMapper systemUserMapper;
+ private final MemberRepository memberRepository;
- private SmsAuthenticationToken getAuthenticationToken(String phone) throws Exception {
+
+ private SmsAuthenticationToken getAuthenticationToken(String phone) throws Exception {
// 手机号查商家
- var merchant = merchantRepository.findBySystemUserPhone(phone).orElse(null);
- if (merchant != null) {
+ var merchant = merchantRepository.findByPhone(phone).orElse(null);
+ if(merchant != null){
var authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
- var loginUser = systemUserMapper.toLoginUserInfo(merchant.getSystemUser());
- return new SmsAuthenticationToken(loginUser, authorities);
+ return new SmsAuthenticationToken(merchant, authorities);
}
- throw new Exception("用户信息不存在");
+ // 手机号查员工
+ var member = memberRepository.findByPhone(phone).orElse(null);
+ if(member != null){
+ var authorities = List.of(new SimpleGrantedAuthority("ROLE_MEMBER"));
+ return new SmsAuthenticationToken(member, authorities);
+ }
+ throw new Exception("用户信息不存在");
}
@Override
diff --git a/src/main/java/com/xjhs/findmemerchant/security/LoginUser.java b/src/main/java/com/xjhs/findmemerchant/security/LoginUser.java
deleted file mode 100644
index 4c7b133..0000000
--- a/src/main/java/com/xjhs/findmemerchant/security/LoginUser.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.xjhs.findmemerchant.security;
-
-import lombok.Data;
-
-/**
- * 登录用户信息
- */
-@Data
-public class LoginUser {
- /**
- * 用户id
- */
- private Long userId;
- /**
- * 手机号
- */
- private String phone;
- /**
- * 关联商户id,不存在则为null
- */
- private Long merchantId;
-
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/security/sms/SmsAuthenticationProvider.java b/src/main/java/com/xjhs/findmemerchant/security/sms/SmsAuthenticationProvider.java
index fe5acfc..199089a 100644
--- a/src/main/java/com/xjhs/findmemerchant/security/sms/SmsAuthenticationProvider.java
+++ b/src/main/java/com/xjhs/findmemerchant/security/sms/SmsAuthenticationProvider.java
@@ -1,31 +1,25 @@
package com.xjhs.findmemerchant.security.sms;
-import com.xjhs.findmemerchant.mapper.SystemUserMapper;
-import com.xjhs.findmemerchant.system.SystemUserService;
+import com.xjhs.findmemerchant.repository.MemberRepository;
+import com.xjhs.findmemerchant.repository.MerchantRepository;
import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
-import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
import java.util.List;
-import java.util.Objects;
-@Slf4j
@Component
@RequiredArgsConstructor
public class SmsAuthenticationProvider implements AuthenticationProvider {
private final SmsCodeService smsCodeService;
- private final SystemUserService systemUserService;
- private final SystemUserMapper systemUserMapper;
+ private final MerchantRepository merchantRepository;
+ private final MemberRepository memberRepository;
@Override
- @Transactional(readOnly = true)
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsAuthenticationToken token = (SmsAuthenticationToken) authentication;
String phone = (String) token.getPrincipal();
@@ -33,17 +27,21 @@ public class SmsAuthenticationProvider implements AuthenticationProvider {
try {
this.smsCodeService.verifyCode(phone,"login" ,code);
} catch (Exception e) {
- throw new CredentialsExpiredException(e.getMessage());
+ throw new UsernameNotFoundException(e.getMessage());
}
- // 取回商家身份
- var systemUser = systemUserService.getAndRegisterByPhone(phone);
- if (Objects.isNull(systemUser.getMerchant())){
- throw new UsernameNotFoundException("手机号码未注册");
+ // 手机号查商家
+ var merchant = merchantRepository.findByPhone(phone).orElse(null);
+ if(merchant != null){
+ var authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
+ return new SmsAuthenticationToken(merchant, authorities);
}
-
- var loginUser = this.systemUserMapper.toLoginUserInfo(systemUser);
- var authorities = List.of(new SimpleGrantedAuthority("ADMIN"));
- return new SmsAuthenticationToken(loginUser, authorities);
+ // 手机号查员工
+ var member = memberRepository.findByPhone(phone).orElse(null);
+ if(member != null){
+ var authorities = List.of(new SimpleGrantedAuthority("ROLE_MEMBER"));
+ return new SmsAuthenticationToken(member, authorities);
+ }
+ throw new UsernameNotFoundException("用户信息不存在");
}
@Override
diff --git a/src/main/java/com/xjhs/findmemerchant/security/sms/SmsCodeService.java b/src/main/java/com/xjhs/findmemerchant/security/sms/SmsCodeService.java
index 25356e9..ca61396 100644
--- a/src/main/java/com/xjhs/findmemerchant/security/sms/SmsCodeService.java
+++ b/src/main/java/com/xjhs/findmemerchant/security/sms/SmsCodeService.java
@@ -17,7 +17,7 @@ public class SmsCodeService {
private static final String SMS_CODE_KEY_PREFIX = "sms:code:";
/** 验证码有效期:5 分钟 */
- private static final Duration SMS_CODE_TTL = Duration.ofMinutes(1);
+ private static final Duration SMS_CODE_TTL = Duration.ofMinutes(5);
private final StringRedisTemplate redisTemplate;
private final Random random = new Random();
@@ -29,10 +29,10 @@ public class SmsCodeService {
}
/**
- * 生成 4 位数字验证码
+ * 生成 6 位数字验证码
*/
private String generateCode() {
- return String.format("%04d", random.nextInt(10000));
+ return String.format("%06d", random.nextInt(1_000_000));
}
/**
@@ -64,6 +64,7 @@ public class SmsCodeService {
// 对齐 Go 里的 ErrSMSCodeExpired
throw new Exception("验证码已过期或未发送");
}
+
if (!realCode.equals(inputCode)) {
// 不正确,但不删除,让用户继续尝试(错误次数由 AuthService 控制)
throw new Exception("验证码错误");
diff --git a/src/main/java/com/xjhs/findmemerchant/service/MerchantService.java b/src/main/java/com/xjhs/findmemerchant/service/MerchantService.java
new file mode 100644
index 0000000..7f3e11b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/service/MerchantService.java
@@ -0,0 +1,85 @@
+package com.xjhs.findmemerchant.service;
+
+import com.xjhs.findmemerchant.dto.MerchantDto;
+import com.xjhs.findmemerchant.mapper.MerchantMapper;
+import com.xjhs.findmemerchant.repository.MerchantRepository;
+import com.xjhs.findmemerchant.types.AuthStatus;
+import com.xjhs.findmemerchant.vo.merchant.MerchantUpdateVo;
+import jakarta.transaction.Transactional;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class MerchantService {
+ public final MerchantRepository merchantRepository;
+ public final MerchantMapper merchantMapper;
+
+ /**
+ * 根据商家id获取商家信息
+ * @param id 商家id
+ * @return 商家信息
+ */
+ public Optional getById(Long id) {
+ return this.merchantRepository.findById(id)
+ .map(this.merchantMapper::toDto);
+ }
+
+ /**
+ * 更新商家信息
+ * @param id 商家id
+ * @param merchantUpdateVo 更新信息
+ * @return 商家信息
+ * @throws Exception 错误信息
+ */
+ @Transactional(rollbackOn = Exception.class)
+ public MerchantDto updateMerchant(Long id, MerchantUpdateVo merchantUpdateVo) throws Exception {
+ var entity = this.merchantRepository.findById(id)
+ .orElseThrow(() -> new Exception("商家信息不存在"));
+ entity.setRealName(merchantUpdateVo.getRealName());
+ this.merchantRepository.save(entity);
+ return this.merchantMapper.toDto(entity);
+ }
+
+ /**
+ * 商家身份证信息验证
+ *
+ * @param id 商家id
+ * @param idCardNo 身份证号
+ * @param realName 真实姓名
+ * @throws Exception 验证异常信息
+ */
+ @Transactional(rollbackOn = Exception.class)
+ public MerchantDto verifyMerchant(Long id, String idCardNo, String realName) throws Exception {
+ var entity = this.merchantRepository.findById(id)
+ .orElseThrow(() -> new Exception("商家信息不存在"));
+ if (entity.getAuthStatus() == AuthStatus.VERIFIED) {
+ throw new Exception("商家信息已认证");
+ }
+ this.idCardVerify(idCardNo, realName);
+ entity.setAuthStatus(AuthStatus.VERIFIED);
+ entity.setIdCardNo(idCardNo);
+ entity.setIdCardEncrypted(idCardNo);
+ entity.setRealName(realName);
+ this.merchantRepository.save(entity);
+ return this.merchantMapper.toDto(entity);
+ }
+
+
+ /**
+ * 第三方验证
+ * @param idCardNo 身份证号码
+ * @param realName 真实姓名
+ * @throws Exception 验证失败信息
+ */
+ public void idCardVerify(String idCardNo, String realName) throws Exception {
+ // TODO: 调用腾讯云身份证二要素核验接口
+ throw new Exception("验证失败,正在开发中");
+ }
+
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/service/StoreService.java b/src/main/java/com/xjhs/findmemerchant/service/StoreService.java
new file mode 100644
index 0000000..acdc4b9
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/service/StoreService.java
@@ -0,0 +1,34 @@
+package com.xjhs.findmemerchant.service;
+
+import com.xjhs.findmemerchant.common.jpa.query.JpaSpecs;
+import com.xjhs.findmemerchant.dto.store.StoreDto;
+import com.xjhs.findmemerchant.mapper.StoreMapper;
+import com.xjhs.findmemerchant.repository.StoreRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StoreService {
+
+ private final StoreRepository storeRepository;
+ private final StoreMapper storeMapper;
+
+ /**
+ * 分页查询
+ *
+ * @param pageable 分页参数
+ * @param merchantId 商家id
+ * @return 分页数据
+ */
+ public Page findPage(Pageable pageable, Long merchantId) {
+ return this.storeRepository.findAll(Specification.allOf(
+ JpaSpecs.eq("merchant.id", merchantId)
+ ), pageable).map(this.storeMapper::toDto);
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/system/SystemUserService.java b/src/main/java/com/xjhs/findmemerchant/system/SystemUserService.java
deleted file mode 100644
index e590d2d..0000000
--- a/src/main/java/com/xjhs/findmemerchant/system/SystemUserService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.xjhs.findmemerchant.system;
-
-import com.xjhs.findmemerchant.system.entity.SystemUser;
-import com.xjhs.findmemerchant.system.repository.SystemUserRepository;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Optional;
-
-@Slf4j
-@Service
-@AllArgsConstructor
-public class SystemUserService {
-
- private final SystemUserRepository systemUserRepository;
- /**
- * 通过手机号获取用户信息,不存在则进行注册
- *
- * @param phone 手机号码
- * @return 用户信息
- */
- public SystemUser getAndRegisterByPhone(String phone) {
- return this.systemUserRepository.findByPhoneAndDeleteTimeIsNull(phone)
- .orElseGet(() -> {
- var systemUser = new SystemUser();
- systemUser.setPhone(phone);
- return this.systemUserRepository.save(systemUser);
- });
- }
-
- public Optional findByPhone(String phone){
- return this.systemUserRepository.findByPhoneAndDeleteTimeIsNull(phone);
- }
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/system/entity/SystemUser.java b/src/main/java/com/xjhs/findmemerchant/system/entity/SystemUser.java
deleted file mode 100644
index 39b4264..0000000
--- a/src/main/java/com/xjhs/findmemerchant/system/entity/SystemUser.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.xjhs.findmemerchant.system.entity;
-
-import com.xjhs.findmemerchant.common.jpa.AbstractBaseEntity;
-import com.xjhs.findmemerchant.entity.Merchant;
-import jakarta.persistence.*;
-import lombok.Getter;
-import lombok.Setter;
-import org.hibernate.annotations.Comment;
-
-/**
- * 用户信息表
- */
-
-@Getter
-@Setter
-@Entity
-@Table
-public class SystemUser extends AbstractBaseEntity {
- @Column(columnDefinition = "CHAR(11)",length = 11)
- @Comment("用户手机号")
- private String phone;
- @Column(length = 20)
- @Comment("用户真实姓名")
- private String realName;
- @Column(length = 18)
- @Comment("身份证号")
- private String idCardNo;
- @OneToOne(mappedBy = "systemUser",cascade = CascadeType.ALL)
- private Merchant merchant;
-
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/system/repository/SystemUserRepository.java b/src/main/java/com/xjhs/findmemerchant/system/repository/SystemUserRepository.java
deleted file mode 100644
index bae01fc..0000000
--- a/src/main/java/com/xjhs/findmemerchant/system/repository/SystemUserRepository.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.xjhs.findmemerchant.system.repository;
-
-import com.xjhs.findmemerchant.system.entity.SystemUser;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-
-import java.util.Optional;
-
-public interface SystemUserRepository extends JpaRepository , JpaSpecificationExecutor {
- Optional findByPhoneAndDeleteTimeIsNull(String phone);
-}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/ActivityStatus.java b/src/main/java/com/xjhs/findmemerchant/types/ActivityStatus.java
new file mode 100644
index 0000000..a0d47a7
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/ActivityStatus.java
@@ -0,0 +1,52 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 活动状态
+ * 0-未开始 1-进行中 2-已结束 3-已下架
+ */
+@Getter
+@AllArgsConstructor
+public enum ActivityStatus {
+ /**
+ * 未开始
+ */
+ NOT_STARTED("未开始"),
+ /**
+ * 进行中
+ */
+ ONGOING("进行中"),
+ /**
+ * 已结束
+ */
+ ENDED("已结束"),
+ /**
+ * 已下架
+ */
+ OFFLINE("已下架");
+
+ private final String desc;
+
+ public static ActivityStatus fromCode(Byte code) {
+ if (code == null) return null;
+
+ return switch (code) {
+ case 0 -> NOT_STARTED;
+ case 1 -> ONGOING;
+ case 2 -> ENDED;
+ case 3 -> OFFLINE;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case NOT_STARTED -> 0;
+ case ONGOING -> 1;
+ case ENDED -> 2;
+ case OFFLINE -> 3;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/ActivityType.java b/src/main/java/com/xjhs/findmemerchant/types/ActivityType.java
new file mode 100644
index 0000000..c07b365
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/ActivityType.java
@@ -0,0 +1,44 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum ActivityType {
+
+ /**
+ * 团购
+ */
+ GROUP_BUY("团购"),
+ /**
+ * 折扣
+ */
+ DISCOUNT("折扣"),
+ /**
+ * 限时优惠
+ */
+ FLASH_SALE("限时优惠");
+
+ private final String desc;
+
+ // 数据库存储 tinyint -> 转为枚举
+ public static ActivityType fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 1 -> GROUP_BUY;
+ case 2 -> DISCOUNT;
+ case 3 -> FLASH_SALE;
+ default -> null;
+ };
+ }
+
+ // 枚举 -> 数据库存储值
+ public byte code() {
+ return switch (this) {
+ case GROUP_BUY -> 1;
+ case DISCOUNT -> 2;
+ case FLASH_SALE -> 3;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/AuthStatus.java b/src/main/java/com/xjhs/findmemerchant/types/AuthStatus.java
new file mode 100644
index 0000000..4c817cd
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/AuthStatus.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 商家实名认证状态
+ * 0-未认证 1-已认证
+ */
+@Getter
+@AllArgsConstructor
+public enum AuthStatus {
+
+ /**
+ * 未认证
+ */
+ NOT_VERIFIED("未认证"),
+ /**
+ * 已认证
+ */
+ VERIFIED("已认证");
+
+ private final String desc;
+
+ public static AuthStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> NOT_VERIFIED;
+ case 1 -> VERIFIED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case NOT_VERIFIED -> 0;
+ case VERIFIED -> 1;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/BusinessLicenseStatus.java b/src/main/java/com/xjhs/findmemerchant/types/BusinessLicenseStatus.java
new file mode 100644
index 0000000..6643d75
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/BusinessLicenseStatus.java
@@ -0,0 +1,47 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 营业执照审核状态
+ * 0-待审核 1-已通过 2-已拒绝
+ */
+@Getter
+@AllArgsConstructor
+public enum BusinessLicenseStatus {
+
+ /**
+ * 待审核
+ */
+ PENDING("待审核"),
+ /**
+ * 已通过
+ */
+ APPROVED("已通过"),
+ /**
+ * 已拒绝
+ */
+ REJECTED("已拒绝");
+
+ private final String desc;
+
+ public static BusinessLicenseStatus fromCode(Byte code) {
+ if (code == null) return null;
+
+ return switch (code) {
+ case 0 -> PENDING;
+ case 1 -> APPROVED;
+ case 2 -> REJECTED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case PENDING -> 0;
+ case APPROVED -> 1;
+ case REJECTED -> 2;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/BusinessStatus.java b/src/main/java/com/xjhs/findmemerchant/types/BusinessStatus.java
new file mode 100644
index 0000000..e8622cc
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/BusinessStatus.java
@@ -0,0 +1,46 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 门店营业状态
+ * 1-营业中 0-已打烊 2-临时打烊
+ */
+@Getter
+@AllArgsConstructor
+public enum BusinessStatus {
+
+ /**
+ * 已打烊
+ */
+ CLOSED("已打烊"), // 0
+ /**
+ * 营业中
+ */
+ OPEN("营业中"), // 1
+ /**
+ * 临时打烊
+ */
+ TEMP_CLOSED("临时打烊"); // 2
+
+ private final String desc;
+
+ public static BusinessStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> CLOSED;
+ case 1 -> OPEN;
+ case 2 -> TEMP_CLOSED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case CLOSED -> 0;
+ case OPEN -> 1;
+ case TEMP_CLOSED -> 2;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/CommonStatus.java b/src/main/java/com/xjhs/findmemerchant/types/CommonStatus.java
new file mode 100644
index 0000000..a98d6a2
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/CommonStatus.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 通用启用/禁用状态
+ * 0-禁用 1-启用
+ */
+@Getter
+@AllArgsConstructor
+public enum CommonStatus {
+
+ /**
+ * 禁用
+ */
+ DISABLED("禁用"),
+ /**
+ * 启用
+ */
+ ENABLED("启用");
+
+ private final String desc;
+
+ public static CommonStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> DISABLED;
+ case 1 -> ENABLED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case DISABLED -> 0;
+ case ENABLED -> 1;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/CouponCodeStatus.java b/src/main/java/com/xjhs/findmemerchant/types/CouponCodeStatus.java
new file mode 100644
index 0000000..bb3e650
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/CouponCodeStatus.java
@@ -0,0 +1,46 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 券码状态
+ * 0-未领取 1-已领取 2-已核销 3-已过期
+ */
+@Getter
+@AllArgsConstructor
+public enum CouponCodeStatus {
+
+ /**
+ * 未领取
+ */
+ UNCLAIMED("未领取"),
+ /**
+ * 已领取
+ */
+ CLAIMED("已领取"),
+ /**
+ * 已核销
+ */
+ VERIFIED("已核销"),
+ /**
+ * 已过期
+ */
+ EXPIRED("已过期");
+
+ private final String desc;
+
+
+ public static CouponCodeStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> UNCLAIMED;
+ case 1 -> CLAIMED;
+ case 2 -> VERIFIED;
+ case 3 -> EXPIRED;
+ default -> null;
+ };
+ }
+
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/CouponStatus.java b/src/main/java/com/xjhs/findmemerchant/types/CouponStatus.java
new file mode 100644
index 0000000..f9ad8fb
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/CouponStatus.java
@@ -0,0 +1,46 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 优惠券状态
+ * 0-下架 1-进行中 2-已结束
+ */
+@Getter
+@AllArgsConstructor
+public enum CouponStatus {
+
+ /**
+ * 下架
+ */
+ OFFLINE("下架"),
+ /**
+ * 进行中
+ */
+ ONLINE("进行中"),
+ /**
+ * 已结束
+ */
+ ENDED("已结束");
+
+ private final String desc;
+
+ public static CouponStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> OFFLINE;
+ case 1 -> ONLINE;
+ case 2 -> ENDED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case OFFLINE -> 0;
+ case ONLINE -> 1;
+ case ENDED -> 2;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/CouponType.java b/src/main/java/com/xjhs/findmemerchant/types/CouponType.java
new file mode 100644
index 0000000..7a4118a
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/CouponType.java
@@ -0,0 +1,58 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 优惠券类型
+ * 1-折扣券 2-满减券 3-现金券 4-赠品券
+ */
+@Getter
+@AllArgsConstructor
+public enum CouponType {
+
+ /**
+ * 折扣券
+ */
+ DISCOUNT("折扣券"),
+ /**
+ * 满减券
+ */
+ REDUCE("满减券"),
+ /**
+ * 现金券
+ */
+ CASH("现金券"),
+ /**
+ * 赠品券
+ */
+ GIFT("赠品券");
+
+ private final String desc;
+
+ /**
+ * 数据库 tinyint -> 枚举
+ */
+ public static CouponType fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 1 -> DISCOUNT;
+ case 2 -> REDUCE;
+ case 3 -> CASH;
+ case 4 -> GIFT;
+ default -> null;
+ };
+ }
+
+ /**
+ * 枚举 -> 数据库 tinyint
+ */
+ public byte code() {
+ return switch (this) {
+ case DISCOUNT -> 1;
+ case REDUCE -> 2;
+ case CASH -> 3;
+ case GIFT -> 4;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/HealthCertificateStatus.java b/src/main/java/com/xjhs/findmemerchant/types/HealthCertificateStatus.java
new file mode 100644
index 0000000..477ae33
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/HealthCertificateStatus.java
@@ -0,0 +1,47 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 健康证状态
+ * 0-待审核 1-有效 2-过期
+ */
+@Getter
+@AllArgsConstructor
+public enum HealthCertificateStatus {
+
+ /**
+ * 待审核
+ */
+ PENDING("待审核"),
+ /**
+ * 有效
+ */
+ VALID("有效"),
+ /**
+ * 已过期
+ */
+ EXPIRED("已过期");
+
+ private final String desc;
+
+ public static HealthCertificateStatus fromCode(Byte code) {
+ if (code == null) return null;
+
+ return switch (code) {
+ case 0 -> PENDING;
+ case 1 -> VALID;
+ case 2 -> EXPIRED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case PENDING -> 0;
+ case VALID -> 1;
+ case EXPIRED -> 2;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/MessageType.java b/src/main/java/com/xjhs/findmemerchant/types/MessageType.java
new file mode 100644
index 0000000..5ed8cd9
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/MessageType.java
@@ -0,0 +1,46 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 消息类型
+ * 1-系统通知 2-活动提醒 3-私信
+ */
+@Getter
+@AllArgsConstructor
+public enum MessageType {
+
+ /**
+ * 系统通知
+ */
+ SYSTEM("系统通知"),
+ /**
+ * 活动提醒
+ */
+ ACTIVITY("活动提醒"),
+ /**
+ * 私信
+ */
+ PRIVATE("私信");
+
+ private final String desc;
+
+ public static MessageType fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 1 -> SYSTEM;
+ case 2 -> ACTIVITY;
+ case 3 -> PRIVATE;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case SYSTEM -> 1;
+ case ACTIVITY -> 2;
+ case PRIVATE -> 3;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/OrderStatus.java b/src/main/java/com/xjhs/findmemerchant/types/OrderStatus.java
new file mode 100644
index 0000000..3f872ec
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/OrderStatus.java
@@ -0,0 +1,65 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 订单状态
+ * 1-待支付 2-已支付 3-已完成 4-已退款 5-已取消
+ */
+@Getter
+@AllArgsConstructor
+public enum OrderStatus {
+
+ /**
+ * 待支付
+ */
+ PENDING("待支付"), // 1
+ /**
+ * 已支付
+ */
+ PAID("已支付"), // 2
+ /**
+ * 已完成
+ */
+ COMPLETED("已完成"), // 3
+ /**
+ * 已退款
+ */
+ REFUNDED("已退款"), // 4
+ /**
+ * 已取消
+ */
+ CANCELLED("已取消"); // 5
+
+ private final String desc;
+
+ public static OrderStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 1 -> PENDING;
+ case 2 -> PAID;
+ case 3 -> COMPLETED;
+ case 4 -> REFUNDED;
+ case 5 -> CANCELLED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case PENDING -> 1;
+ case PAID -> 2;
+ case COMPLETED -> 3;
+ case REFUNDED -> 4;
+ case CANCELLED -> 5;
+ };
+ }
+
+ /**
+ * 数值比较用
+ */
+ public int getCodeValue() {
+ return code();
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/ProductStatus.java b/src/main/java/com/xjhs/findmemerchant/types/ProductStatus.java
new file mode 100644
index 0000000..4f0f015
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/ProductStatus.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 商品状态
+ * 0-下架 1-上架
+ */
+@Getter
+@AllArgsConstructor
+public enum ProductStatus {
+
+ /**
+ * 已下架
+ */
+ OFF_SALE("已下架"),
+ /**
+ * 已上架
+ */
+ ON_SALE("已上架");
+
+ private final String desc;
+
+ public static ProductStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> OFF_SALE;
+ case 1 -> ON_SALE;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case OFF_SALE -> 0;
+ case ON_SALE -> 1;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/ReviewStatus.java b/src/main/java/com/xjhs/findmemerchant/types/ReviewStatus.java
new file mode 100644
index 0000000..b5fcd6d
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/ReviewStatus.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 评价状态
+ * 1-正常 0-隐藏
+ */
+@Getter
+@AllArgsConstructor
+public enum ReviewStatus {
+
+ /**
+ * 隐藏
+ */
+ HIDDEN("隐藏"),
+ /**
+ * 正常
+ */
+ NORMAL("正常");
+
+ private final String desc;
+
+ public static ReviewStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> HIDDEN;
+ case 1 -> NORMAL;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case HIDDEN -> 0;
+ case NORMAL -> 1;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/SettlementStatus.java b/src/main/java/com/xjhs/findmemerchant/types/SettlementStatus.java
new file mode 100644
index 0000000..d9caf44
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/SettlementStatus.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 结算状态
+ * 0-待结算 1-已结算
+ */
+@Getter
+@AllArgsConstructor
+public enum SettlementStatus {
+
+ /**
+ * 待结算
+ */
+ PENDING("待结算"),
+ /**
+ * 已结算
+ */
+ SETTLED("已结算");
+
+ private final String desc;
+
+ public static SettlementStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> PENDING;
+ case 1 -> SETTLED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case PENDING -> 0;
+ case SETTLED -> 1;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/SettlementType.java b/src/main/java/com/xjhs/findmemerchant/types/SettlementType.java
new file mode 100644
index 0000000..43536a7
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/SettlementType.java
@@ -0,0 +1,40 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 结算类型
+ * 1-日结 2-周结
+ */
+@Getter
+@AllArgsConstructor
+public enum SettlementType {
+
+ /**
+ * 日结
+ */
+ DAILY("日结"),
+ /**
+ * 周结
+ */
+ WEEKLY("周结");
+
+ private final String desc;
+
+ public static SettlementType fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 1 -> DAILY;
+ case 2 -> WEEKLY;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case DAILY -> 1;
+ case WEEKLY -> 2;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/StoreAuditStatus.java b/src/main/java/com/xjhs/findmemerchant/types/StoreAuditStatus.java
new file mode 100644
index 0000000..1f8fe0b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/StoreAuditStatus.java
@@ -0,0 +1,46 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 门店审核状态
+ * 0-待审核 1-已通过 2-已拒绝
+ */
+@Getter
+@AllArgsConstructor
+public enum StoreAuditStatus {
+
+ /**
+ * 待审核
+ */
+ PENDING("待审核"),
+ /**
+ * 已通过
+ */
+ APPROVED("已通过"),
+ /**
+ * 已拒绝
+ */
+ REJECTED("已拒绝");
+
+ private final String desc;
+
+ public static StoreAuditStatus fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 0 -> PENDING;
+ case 1 -> APPROVED;
+ case 2 -> REJECTED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case PENDING -> 0;
+ case APPROVED -> 1;
+ case REJECTED -> 2;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/TransactionType.java b/src/main/java/com/xjhs/findmemerchant/types/TransactionType.java
new file mode 100644
index 0000000..50b7f86
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/TransactionType.java
@@ -0,0 +1,58 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 钱包交易类型
+ * 1-收入 2-支出 3-冻结 4-解冻 5-提现
+ */
+@Getter
+@AllArgsConstructor
+public enum TransactionType {
+
+ /**
+ * 收入
+ */
+ INCOME("收入"), // 1
+ /**
+ * 支出
+ */
+ EXPENSE("支出"), // 2
+ /**
+ * 冻结
+ */
+ FREEZE("冻结"), // 3
+ /**
+ * 解冻
+ */
+ UNFREEZE("解冻"), // 4
+ /**
+ * 提现
+ */
+ WITHDRAW("提现"); // 5
+
+ private final String desc;
+
+ public static TransactionType fromCode(Byte code) {
+ if (code == null) return null;
+ return switch (code) {
+ case 1 -> INCOME;
+ case 2 -> EXPENSE;
+ case 3 -> FREEZE;
+ case 4 -> UNFREEZE;
+ case 5 -> WITHDRAW;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case INCOME -> 1;
+ case EXPENSE -> 2;
+ case FREEZE -> 3;
+ case UNFREEZE -> 4;
+ case WITHDRAW -> 5;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/types/WithdrawalStatus.java b/src/main/java/com/xjhs/findmemerchant/types/WithdrawalStatus.java
new file mode 100644
index 0000000..400a6f1
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/types/WithdrawalStatus.java
@@ -0,0 +1,59 @@
+package com.xjhs.findmemerchant.types;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 提现状态
+ * 0-待审核 1-处理中 2-已完成 3-已拒绝 4-已取消
+ */
+@Getter
+@AllArgsConstructor
+public enum WithdrawalStatus {
+
+ /**
+ * 待审核
+ */
+ PENDING("待审核"),
+ /**
+ * 处理中
+ */
+ PROCESSING("处理中"),
+ /**
+ * 已完成
+ */
+ COMPLETED("已完成"),
+ /**
+ * 已拒绝
+ */
+ REJECTED("已拒绝"),
+ /**
+ * 已取消
+ */
+ CANCELLED("已取消");
+
+ private final String desc;
+
+ public static WithdrawalStatus fromCode(Byte code) {
+ if (code == null) return null;
+
+ return switch (code) {
+ case 0 -> PENDING;
+ case 1 -> PROCESSING;
+ case 2 -> COMPLETED;
+ case 3 -> REJECTED;
+ case 4 -> CANCELLED;
+ default -> null;
+ };
+ }
+
+ public byte code() {
+ return switch (this) {
+ case PENDING -> 0;
+ case PROCESSING -> 1;
+ case COMPLETED -> 2;
+ case REJECTED -> 3;
+ case CANCELLED -> 4;
+ };
+ }
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/auth/RegisterVo.java b/src/main/java/com/xjhs/findmemerchant/vo/auth/RegisterVo.java
index 5270338..c0d031c 100644
--- a/src/main/java/com/xjhs/findmemerchant/vo/auth/RegisterVo.java
+++ b/src/main/java/com/xjhs/findmemerchant/vo/auth/RegisterVo.java
@@ -19,6 +19,6 @@ public class RegisterVo {
* 短信验证码
*/
@NotBlank
- @Size(max = 4, min = 4)
+ @Size(max = 6, min = 6)
private String code;
}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/member/EmployeeCreateVo.java b/src/main/java/com/xjhs/findmemerchant/vo/member/EmployeeCreateVo.java
new file mode 100644
index 0000000..e412ccc
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/vo/member/EmployeeCreateVo.java
@@ -0,0 +1,37 @@
+package com.xjhs.findmemerchant.vo.member;
+
+import com.xjhs.findmemerchant.common.jackson.JsonLong;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+@Data
+public class EmployeeCreateVo {
+ /**
+ * 员工姓名
+ */
+ @NotBlank(message = "name 不能为空")
+ @Size(min = 2, max = 50, message = "name 长度必须在 2~50 之间")
+ private String name;
+ /**
+ * 员工手机号
+ */
+ @NotBlank(message = "phone 不能为空")
+ @Pattern(regexp = "^\\d{11}$", message = "phone 必须是 11 位数字")
+ private String phone;
+ /**
+ * 角色id(前端传递请使用 string 类型)
+ */
+ @NotNull(message = "roleId 不能为空")
+ @JsonLong
+ private Long roleId;
+ /**
+ * 角色id(前端传递请使用 string 类型)
+ */
+ @NotNull(message = "storeId 不能为空")
+ @JsonLong
+ @Deprecated(since = "多余的")
+ private Long storeId;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/member/EmployeeUpdateVo.java b/src/main/java/com/xjhs/findmemerchant/vo/member/EmployeeUpdateVo.java
new file mode 100644
index 0000000..64bcfc2
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/vo/member/EmployeeUpdateVo.java
@@ -0,0 +1,22 @@
+package com.xjhs.findmemerchant.vo.member;
+
+import com.xjhs.findmemerchant.common.jackson.JsonLong;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * 员工更新参数
+ */
+@Data
+public class EmployeeUpdateVo {
+ /**
+ * 员工姓名
+ */
+ @Size(min = 2,max = 50)
+ private String name;
+ /**
+ * 员工角色(前端传递string类型)
+ */
+ @JsonLong
+ private Long roleId;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/merchant/MerchantUpdateVo.java b/src/main/java/com/xjhs/findmemerchant/vo/merchant/MerchantUpdateVo.java
index 34b917a..bb5dfde 100644
--- a/src/main/java/com/xjhs/findmemerchant/vo/merchant/MerchantUpdateVo.java
+++ b/src/main/java/com/xjhs/findmemerchant/vo/merchant/MerchantUpdateVo.java
@@ -2,7 +2,13 @@ package com.xjhs.findmemerchant.vo.merchant;
import lombok.Data;
+/**
+ * 商家信息更新对象
+ */
@Data
public class MerchantUpdateVo {
-
+ /**
+ * 真实姓名
+ */
+ private String realName;
}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/merchant/MerchantVerifyVo.java b/src/main/java/com/xjhs/findmemerchant/vo/merchant/MerchantVerifyVo.java
new file mode 100644
index 0000000..22ed468
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/vo/merchant/MerchantVerifyVo.java
@@ -0,0 +1,24 @@
+package com.xjhs.findmemerchant.vo.merchant;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * 商家验证参数
+ */
+@Data
+public class MerchantVerifyVo {
+ /**
+ * 身份证号
+ */
+ @NotBlank
+ @Size(min = 18, max = 18)
+ private String idCardNo;
+ /**
+ * 真实姓名
+ */
+ @NotBlank
+ @Size(min = 2, max = 50)
+ private String realName;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/store/BusinessPeriodVo.java b/src/main/java/com/xjhs/findmemerchant/vo/store/BusinessPeriodVo.java
new file mode 100644
index 0000000..e6fc08b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/vo/store/BusinessPeriodVo.java
@@ -0,0 +1,30 @@
+package com.xjhs.findmemerchant.vo.store;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+@Data
+public class BusinessPeriodVo {
+ /**
+ * 周几,周天0,...
+ */
+ @Size(max = 6)
+ @NotNull
+ private Integer dayOfWeek;
+ /**
+ * 开始营业时间 HH:mm 格式
+ */
+ @NotBlank
+ private String startTime;
+ /**
+ * 结束营业时间 HH:mm 格式
+ */
+ @NotBlank
+ private String endTime;
+ /**
+ * 是否启用
+ */
+ private Boolean enabled;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/store/StoreBusinessStatusUpdateVo.java b/src/main/java/com/xjhs/findmemerchant/vo/store/StoreBusinessStatusUpdateVo.java
new file mode 100644
index 0000000..459133b
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/vo/store/StoreBusinessStatusUpdateVo.java
@@ -0,0 +1,24 @@
+package com.xjhs.findmemerchant.vo.store;
+
+import com.xjhs.findmemerchant.types.BusinessStatus;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class StoreBusinessStatusUpdateVo {
+ /**
+ * 营业状态
+ */
+ @NotNull
+ private BusinessStatus businessStatus;
+
+ /**
+ * 临时关闭原因(可选;temp_closed 时使用)
+ */
+ private String reason;
+
+ /**
+ * 重新营业时间(RFC3339 字符串,可选)
+ */
+ private String until;
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/store/StoreCreateVo.java b/src/main/java/com/xjhs/findmemerchant/vo/store/StoreCreateVo.java
new file mode 100644
index 0000000..4bc7741
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/vo/store/StoreCreateVo.java
@@ -0,0 +1,57 @@
+package com.xjhs.findmemerchant.vo.store;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * 创建门店参数
+ */
+@Data
+public class StoreCreateVo {
+ /**
+ * 门店名称
+ */
+ @NotBlank
+ @Size(min = 2, max = 100)
+ private String name;
+ /**
+ * 门店logo
+ */
+ private String logo;
+ /**
+ * 联系电话
+ */
+ @NotBlank
+ @Size(min = 11, max = 11)
+ private String phone;
+ /**
+ * 省份
+ */
+ @NotBlank
+ @Size(max = 50)
+ private String province;
+ /**
+ * 市
+ */
+ @NotBlank
+ @Size(max = 50)
+ private String city;
+ /**
+ * 街道
+ */
+ @NotBlank
+ @Size(max = 50)
+ private String district;
+ /**
+ * 地址
+ */
+ @NotBlank
+ @Size(max = 200)
+ private String address;
+ /**
+ * 营业时间
+ */
+ private String businessHours;
+
+}
diff --git a/src/main/java/com/xjhs/findmemerchant/vo/store/StoreUpdateVo.java b/src/main/java/com/xjhs/findmemerchant/vo/store/StoreUpdateVo.java
new file mode 100644
index 0000000..97ad487
--- /dev/null
+++ b/src/main/java/com/xjhs/findmemerchant/vo/store/StoreUpdateVo.java
@@ -0,0 +1,66 @@
+package com.xjhs.findmemerchant.vo.store;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * 门店更新参数
+ */
+@Data
+public class StoreUpdateVo {
+ /**
+ * 门店名称
+ */
+ @NotBlank
+ @Size(max = 100, message = "name长度不能超过100")
+ private String name;
+ /**
+ * 门店logo
+ */
+ @Size(max = 255, message = "logo长度不能超过255")
+ private String logo;
+
+ /**
+ * 联系电话
+ */
+ // @Pattern(regexp = "^(\\+\\d{1,3})?\\d{6,20}$", message = "phone格式不正确")
+ @NotBlank
+ @Size(min = 11, max = 11)
+ private String phone;
+ /**
+ * 省
+ */
+ @NotBlank
+ @Size(max = 50, message = "province长度不能超过50")
+ private String province;
+ /**
+ * 市
+ */
+ @NotBlank
+ @Size(max = 50, message = "city长度不能超过50")
+ private String city;
+ /**
+ * 区
+ */
+ @NotBlank
+ @Size(max = 50, message = "district长度不能超过50")
+ private String district;
+ /**
+ * 地址
+ */
+ @NotBlank
+ @Size(max = 255, message = "address长度不能超过255")
+ private String address;
+ /**
+ * 营业时间
+ */
+ @Size(max = 100, message = "businessHours长度不能超过100")
+ private String businessHours;
+
+ /**
+ * 验证码(先按常见4~8位数字)
+ */
+ @NotBlank
+ private String phoneVerifyCode;
+}
diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml
new file mode 100644
index 0000000..074d7f7
--- /dev/null
+++ b/src/main/resources/application-local.yml
@@ -0,0 +1,14 @@
+
+spring:
+ datasource:
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://192.168.1.20:3306/findme-merchant?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+ username: root
+ password: 123456
+ data:
+ redis:
+ host: 192.168.1.20
+ port: 6379
+ password: 123456
+ database: 0
+ timeout: 3s
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 83feb8c..8c6d574 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,6 +1,6 @@
spring:
profiles:
- active: default
+ active: default,local
application:
name: findme-backend-merchant
main:
@@ -21,30 +21,13 @@ spring:
format_sql: true
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://${MYSQL_HOST:8.137.94.92}:${MYSQL_PORT:23306}/findme-merchant?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+ url: jdbc:mysql://${MYSQL_HOST:192.168.1.20}:${MYSQL_PORT:3306}/findme-merchant?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: ${MYSQL_USER:root}
- password: ${MYSQL_PASS:pW2CwlHte3%^#y8Z}
+ password: ${MYSQL_PASS:123456}
data:
redis:
- host: ${REDIS_HOST:8.137.94.92}
- port: ${REDIS_PORT:26379}
- password: ${REDIS_PASS:9Hii(AaVzTNY<%e}
+ host: ${REDIS_HOST:192.168.1.20}
+ port: ${REDIS_PORT:6379}
+ password: ${REDIS_PASS:123456}
database: 0
- timeout: 3s
-
-appconfig:
- saveFileRoot: ${SAVE_FILE_ROOT:./file-data}
- amapKey: ${AMAP_KEY:c618de6e686c43095a8593db836c7de2}
- tencentSms:
- sdkAppId: ${SMS_SDKAPP_ID:1401031336}
- templateId: ${SMS_TEMPLATE_ID:2512787}
- signName: ${SMS_SIGN_NAME:新疆火烁智能科技}
- secretId: ${SMS_SECRET_ID:AKIDWyGMFwQinhPFzXt54rTuAD5kEYheOyOd}
- secretKey: ${SMS_SECRET_KEY:62mLjvwmQs9GAsQ5f6LQ7umgWCgy31sX}
- tencentCos:
- secretId: ${COS_SECRET_ID:AKIDWyGMFwQinhPFzXt54rTuAD5kEYheOyOd}
- secretKey: ${COS_SECRET_KEY:62mLjvwmQs9GAsQ5f6LQ7umgWCgy31sX}
- appId: ${COS_APP_ID:1375214531}
- bucketName: ${COS_APP_BUCKET:merchant-1375214531}
-
-
+ timeout: 3s
\ No newline at end of file