Преглед на файлове

Merge remote-tracking branch 'origin/store-plantform' into store-plantform

qrs преди 3 седмици
родител
ревизия
eae56138a1
променени са 38 файла, в които са добавени 4567 реда и са изтрити 586 реда
  1. 0 57
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreLicenseHistoryDTO.java
  2. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreIncomeDetailsRecordVo.java
  3. 5 2
      alien-entity/src/main/java/shop/alien/entity/storePlatform/StoreLicenseHistory.java
  4. 35 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreLicenseHistoryDto.java
  5. 1 1
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreLicenseHistoryVO.java
  6. 1 1
      alien-entity/src/main/java/shop/alien/mapper/StoreIncomeDetailsRecordMapper.java
  7. 0 17
      alien-entity/src/main/java/shop/alien/mapper/StoreLicenseHistoryMapper.java
  8. 33 0
      alien-entity/src/main/java/shop/alien/mapper/storePlantform/StoreLicenseHistoryMapper.java
  9. 3 3
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/CouponManageController.java
  10. 222 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/IncomeManageController.java
  11. 55 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LicenseController.java
  12. 59 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/MerchantUserController.java
  13. 1 1
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/NoticeController.java
  14. 0 171
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StoreLicenseHistoryController.java
  15. 25 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/dto/AddAlipayAccountDTO.java
  16. 3 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/dto/MarkAllNoticesReadDTO.java
  17. 25 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/dto/SetPayPasswordDTO.java
  18. 3 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/example/LoginUserUtilExample.java
  19. 1 2
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/CouponManageService.java
  20. 110 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/IncomeManageService.java
  21. 18 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/LicenseService.java
  22. 26 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/MerchantUserService.java
  23. 0 79
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/StoreLicenseHistoryService.java
  24. 6 4
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/CouponManageServiceImpl.java
  25. 1005 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/IncomeManageServiceImpl.java
  26. 197 5
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LicenseServiceImpl.java
  27. 219 2
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/MerchantUserServiceImpl.java
  28. 0 241
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreLicenseHistoryServiceImpl.java
  29. 3 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/util/LoginUserUtil.java
  30. 5 0
      alien-store-platform/接口文档/19-查询账户余额接口.md
  31. 5 0
      alien-store-platform/接口文档/20-修改商户用户信息接口.md
  32. 1 0
      alien-store-platform/接口文档/21-检查支付密码接口.md
  33. 1 0
      alien-store-platform/接口文档/22-批量标记通知已读接口-DTO版.md
  34. 930 0
      alien-store-platform/接口文档/29-重置到刚注册状态接口.md
  35. 790 0
      alien-store-platform/接口文档/30-已入账未入账详情查询接口.md
  36. 725 0
      alien-store-platform/接口文档/31-收入汇总统计接口.md
  37. 15 0
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  38. 35 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

+ 0 - 57
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreLicenseHistoryDTO.java

@@ -1,57 +0,0 @@
-package shop.alien.entity.store.dto;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.util.Date;
-
-/**
- * 商户证照历史记录DTO
- *
- * @author system
- * @since 2025-11-24
- */
-@Data
-@JsonInclude
-@ApiModel(value = "StoreLicenseHistoryDTO对象", description = "商户证照历史记录DTO")
-public class StoreLicenseHistoryDTO {
-
-    @ApiModelProperty(value = "主键")
-    private Integer id;
-
-    @ApiModelProperty(value = "商户ID", required = true)
-    private Integer storeId;
-
-    @ApiModelProperty(value = "证照类型: 1-合同管理, 2-食品经营许可证", required = true)
-    private Integer licenseStatus;
-
-    @ApiModelProperty(value = "审核状态: 1-审核中, 2-审核拒绝, 3-审核通过", required = true)
-    private Integer licenseExecuteStatus;
-
-    @ApiModelProperty(value = "图片URL", required = true)
-    private String imgUrl;
-
-    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
-    private Integer deleteFlag;
-
-    @ApiModelProperty(value = "创建时间")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date createdTime;
-
-    @ApiModelProperty(value = "创建人ID")
-    private Integer createdUserId;
-
-    @ApiModelProperty(value = "修改时间")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date updatedTime;
-
-    @ApiModelProperty(value = "修改人ID")
-    private Integer updatedUserId;
-}
-

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreIncomeDetailsRecordVo.java

@@ -30,6 +30,10 @@ public class StoreIncomeDetailsRecordVo extends StoreIncomeDetailsRecord {
     @ApiModelProperty(value = "收入金额")
     String incomeMoney;
 
+    @ApiModelProperty(value = "订单价格(原价)")
+    String price;
+
+
     @ApiModelProperty(value = "手续费Str")
     String commissionStr;
 

+ 5 - 2
alien-entity/src/main/java/shop/alien/entity/store/StoreLicenseHistory.java → alien-entity/src/main/java/shop/alien/entity/storePlatform/StoreLicenseHistory.java

@@ -1,4 +1,4 @@
-package shop.alien.entity.store;
+package shop.alien.entity.storePlatform;
 
 import com.baomidou.mybatisplus.annotation.*;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
@@ -42,9 +42,12 @@ public class StoreLicenseHistory extends Model<StoreLicenseHistory> {
     @TableField("img_url")
     private String imgUrl;
 
+    @ApiModelProperty(value = "拒绝原因")
+    @TableField("reason_refusal")
+    private String reasonRefusal;
+
     @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
     @TableField("delete_flag")
-    @TableLogic
     private Integer deleteFlag;
 
     @ApiModelProperty(value = "创建时间")

+ 35 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreLicenseHistoryDto.java

@@ -0,0 +1,35 @@
+package shop.alien.entity.storePlatform.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.apache.ibatis.type.Alias;
+import shop.alien.entity.storePlatform.StoreLicenseHistory;
+
+/**
+ * 商户证照历史记录展示DTO
+ * <p>
+ * 继承自 StoreLicenseHistory,添加了格式化的展示字段
+ * 注意:此类不用于数据库操作,仅用于数据展示
+ * </p>
+ *
+ * @author system
+ * @since 2025-11-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@JsonInclude
+@Alias("LicenseHistoryDisplayDto")  // 指定 MyBatis 别名,避免与 StoreLicenseHistoryDTO 冲突
+@ApiModel(value = "StoreLicenseHistoryDto对象", description = "商户证照历史记录展示DTO")
+public class StoreLicenseHistoryDto extends StoreLicenseHistory {
+
+    @ApiModelProperty(value = "格式化后的创建时间 (yyyy-MM-dd HH:mm:ss)")
+    private String createdDateFormat;
+
+    @ApiModelProperty(value = "审核状态名称")
+    private String licenseExecuteName;
+
+}
+

+ 1 - 1
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreLicenseHistoryVO.java → alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreLicenseHistoryVO.java

@@ -1,4 +1,4 @@
-package shop.alien.entity.store.vo;
+package shop.alien.entity.storePlatform.vo;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;

+ 1 - 1
alien-entity/src/main/java/shop/alien/mapper/StoreIncomeDetailsRecordMapper.java

@@ -20,7 +20,7 @@ import java.util.List;
 public interface StoreIncomeDetailsRecordMapper extends BaseMapper<StoreIncomeDetailsRecord> {
 
     @Select("select income.id, income.money, income.commission, income.created_time checkTime, uorder.created_time orderTime, date_add(income.created_time, interval 3 day) incomeTime, " +
-            "       income_type, uorder.quan_code, coupon.name couponName, store.store_name, if(lfo.store_id is not null ,'true','false') refundType " +
+            "       income_type, uorder.quan_code,  uorder.price, coupon.name couponName, coupon.coupon_code, store.store_name, if(lfo.store_id is not null ,'true','false') refundType " +
             "from store_income_details_record income " +
             "join store_info store on store.id = income.store_id " +
             "left join life_user_order uorder on uorder.id = income.user_order_id " +

+ 0 - 17
alien-entity/src/main/java/shop/alien/mapper/StoreLicenseHistoryMapper.java

@@ -1,17 +0,0 @@
-package shop.alien.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-import shop.alien.entity.store.StoreLicenseHistory;
-
-/**
- * 商户证照历史记录 Mapper 接口
- *
- * @author system
- * @since 2025-11-24
- */
-@Mapper
-public interface StoreLicenseHistoryMapper extends BaseMapper<StoreLicenseHistory> {
-
-}
-

+ 33 - 0
alien-entity/src/main/java/shop/alien/mapper/storePlantform/StoreLicenseHistoryMapper.java

@@ -0,0 +1,33 @@
+package shop.alien.mapper.storePlantform;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import shop.alien.entity.storePlatform.StoreLicenseHistory;
+
+import java.util.List;
+
+/**
+ * 商户证照历史记录 Mapper 接口
+ *
+ * @author system
+ * @since 2025-11-24
+ */
+public interface StoreLicenseHistoryMapper extends BaseMapper<StoreLicenseHistory> {
+
+    /**
+     * 根据商户ID、证照类型和删除标记查询证照历史记录列表
+     *
+     * @param storeId 商户ID
+     * @param licenseStatus 证照类型(1-合同管理,2-食品经营许可证)
+     * @param deleteFlag 删除标记(0-未删除,1-已删除)
+     * @return 证照历史记录列表
+     */
+    @Select("SELECT * FROM store_license_history WHERE store_id = #{storeId} AND license_status = #{licenseStatus} AND delete_flag = #{deleteFlag} ORDER BY created_time DESC")
+    List<StoreLicenseHistory> queryLicenceByStatusList(@Param("storeId") Integer storeId, 
+                                                        @Param("licenseStatus") Integer licenseStatus, 
+                                                        @Param("deleteFlag") Integer deleteFlag);
+
+}
+

+ 3 - 3
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/CouponManageController.java

@@ -49,11 +49,11 @@ public class CouponManageController {
             @ApiImplicitParam(name = "quanCode", value = "券码", dataType = "String", paramType = "query", required = true)
     })
     @GetMapping("/verifyCoupon")
-    public R<Map<String, String>> verifyCoupon(@RequestParam("storeId") String storeId,
+    public R<Map<String, String>> verifyCoupon(
                                                 @RequestParam("quanCode") String quanCode) {
-        log.info("CouponManageController.verifyCoupon?storeId={}, quanCode={}", storeId, quanCode);
+        log.info("CouponManageController.verifyCoupon? quanCode={}", quanCode);
         try {
-            Map<String, String> result = couponManageService.verifyCoupon(storeId, quanCode);
+            Map<String, String> result = couponManageService.verifyCoupon(quanCode);
             return R.data(result);
         } catch (Exception e) {
             log.error("CouponManageController.verifyCoupon ERROR: {}", e.getMessage(), e);

+ 222 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/IncomeManageController.java

@@ -0,0 +1,222 @@
+package shop.alien.storeplatform.controller;
+
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.service.IncomeManageService;
+import shop.alien.storeplatform.util.LoginUserUtil;
+
+import java.util.Map;
+
+/**
+ * web端商户收入管理 前端控制器
+ *
+ * @author ssk
+ * @since 2025-11-14
+ */
+@Slf4j
+@Api(tags = {"web端商户收入管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/incomeManage")
+@RequiredArgsConstructor
+public class IncomeManageController {
+
+    private final IncomeManageService incomeManageService;
+
+    @ApiOperation("账期查询")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "incomeType", value = "收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "paymentType", value = "账期类型, 0:未到, 1:已到", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "startTime", value = "开始时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "endTime", value = "结束时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "每页条数", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getPaymentCycle")
+    public R<JSONObject> getPaymentCycle(
+            @RequestParam(value = "incomeType", required = false) Integer incomeType,
+            @RequestParam(value = "paymentType", required = false) Integer paymentType,
+            @RequestParam(value = "startTime", required = false) String startTime,
+            @RequestParam(value = "endTime", required = false) String endTime,
+            @RequestParam(value = "page", defaultValue = "1") int page,
+            @RequestParam(value = "size", defaultValue = "10") int size) {
+        log.info("IncomeManageController.getPaymentCycle? incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
+                incomeType, paymentType, startTime, endTime, page, size);
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            JSONObject result = incomeManageService.getPaymentCycle(storeId, incomeType, paymentType, startTime, endTime, page, size);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getPaymentCycle ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("查询账户余额")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getAccountBalance")
+    public R<Map<String, Object>> getAccountBalance() {
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            Map<String, Object> result = incomeManageService.getAccountBalance(storeId);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getAccountBalance ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("提现申请")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "payPassword", value = "支付密码", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "withdrawalMoney", value = "提现金额(单位:分)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/cashOut")
+    public R<?> cashOut(
+            @RequestParam("payPassword") String payPassword,
+            @RequestParam("withdrawalMoney") Integer withdrawalMoney) {
+        log.info("IncomeManageController.cashOut? payPassword=***, withdrawalMoney={}",
+                 withdrawalMoney);
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            return incomeManageService.cashOut(storeId, payPassword, withdrawalMoney);
+        } catch (Exception e) {
+            log.error("IncomeManageController.cashOut ERROR: {}", e.getMessage(), e);
+            return R.fail("提现失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("提现记录查询")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "cashOutStartTime", value = "开始时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "cashOutEndTime", value = "结束时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "paymentStatus", value = "提现状态(1-待审核, 2-审核不通过, 3-已通过, 4-已打款, 5-打款失败)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "每页条数", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getCashOutRecordList")
+    public R<?> getCashOutRecordList(
+            @RequestParam(value = "cashOutStartTime", required = false) String cashOutStartTime,
+            @RequestParam(value = "cashOutEndTime", required = false) String cashOutEndTime,
+            @RequestParam(value = "paymentStatus", required = false) String paymentStatus,
+            @RequestParam(value = "page", defaultValue = "1") Integer page,
+            @RequestParam(value = "size", defaultValue = "10") Integer size) {
+        log.info("IncomeManageController.getCashOutRecordList? cashOutStartTime={}, cashOutEndTime={}, paymentStatus={}, page={}, size={}",
+                 cashOutStartTime, cashOutEndTime, paymentStatus, page, size);
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            Object result = incomeManageService.getCashOutRecordList(storeId, cashOutStartTime, cashOutEndTime, paymentStatus, page, size);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getCashOutRecordList ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("团购收益查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "date", value = "日期(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "incomeType", value = "收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "每页条数", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getGroupIncome")
+    public R<?> getGroupIncome(
+            @RequestParam("date") String date,
+            @RequestParam(value = "incomeType", required = false) Integer incomeType,
+            @RequestParam(value = "page", defaultValue = "1") Integer page,
+            @RequestParam(value = "size", defaultValue = "10") Integer size) {
+        log.info("IncomeManageController.getGroupIncome?date={}, incomeType={}, page={}, size={}",
+                date, incomeType, page, size);
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            Object result = incomeManageService.getGroupIncome(storeId, date, incomeType, page, size);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getGroupIncome ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("快速提现申请(免审核)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "payPassword", value = "支付密码", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "withdrawalMoney", value = "提现金额(单位:分)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/applyFastCashOut")
+    public R<?> applyFastCashOut(
+            @RequestParam("payPassword") String payPassword,
+            @RequestParam("withdrawalMoney") Integer withdrawalMoney) {
+        log.info("IncomeManageController.applyFastCashOut? payPassword=***, withdrawalMoney={}",
+                withdrawalMoney);
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            return incomeManageService.applyFastCashOut(storeId, payPassword, withdrawalMoney);
+        } catch (Exception e) {
+            log.error("IncomeManageController.applyFastCashOut ERROR: {}", e.getMessage(), e);
+            return R.fail("快速提现失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("已入账/未入账详情查询")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "incomeType", value = "收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "paymentType", value = "账期类型, 0:未入账, 1:已入账", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "startTime", value = "开始时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "endTime", value = "结束时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "page", value = "页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "每页条数", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getPaymentDetails")
+    public R<com.alibaba.fastjson2.JSONObject> getPaymentDetails(
+            @RequestParam(value = "incomeType", required = false) Integer incomeType,
+            @RequestParam("paymentType") Integer paymentType,
+            @RequestParam("startTime") String startTime,
+            @RequestParam("endTime") String endTime,
+            @RequestParam(value = "page", defaultValue = "1") int page,
+            @RequestParam(value = "size", defaultValue = "10") int size) {
+        log.info("IncomeManageController.getPaymentDetails? incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
+                incomeType, paymentType, startTime, endTime, page, size);
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            com.alibaba.fastjson2.JSONObject result = incomeManageService.getPaymentDetails(
+                    storeId, incomeType, paymentType, startTime, endTime, page, size);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getPaymentDetails ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("收入汇总统计")
+    @ApiOperationSupport(order = 8)
+    @GetMapping("/getIncomeSummary")
+    public R<Map<String, Object>> getIncomeSummary() {
+        log.info("IncomeManageController.getIncomeSummary - 查询收入汇总统计");
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            Map<String, Object> result = incomeManageService.getIncomeSummary(storeId);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getIncomeSummary ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+}
+

+ 55 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LicenseController.java

@@ -6,6 +6,10 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.WebAudit;
+import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryDto;
+import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
+import shop.alien.mapper.WebAuditMapper;
 import shop.alien.storeplatform.service.LicenseService;
 
 import java.util.List;
@@ -21,6 +25,10 @@ import java.util.List;
 public class LicenseController {
 
     private final LicenseService licenseService;
+
+    private final WebAuditMapper webAuditMapper;
+
+
     @ApiOperation("获取营业执照图片信息")
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({
@@ -57,6 +65,53 @@ public class LicenseController {
         return R.data(result);
     }
 
+    @ApiOperation(value = "门店装修-食品经营许可证")
+    @PostMapping("/uploadfoodLicence")
+    public R<String> uploadfoodLicence(@RequestBody StoreImg storeImg) {
+        log.info("StoreInfoController.uploadfoodLicence?storeImg={}", storeImg);
+        int num = licenseService.uploadfoodLicence(storeImg);
+        if (num > 0) {
+            WebAudit webAudit = new WebAudit();
+            webAudit.setStoreInfoId(storeImg.getStoreId().toString());
+            webAudit.setType("7");
+            webAudit.setStatus("0");
+            webAudit.setContent("经营许可证");
+            webAuditMapper.insert(webAudit);
+            return R.success("食品经营许可证图片添加成功");
+        }
+        return R.fail("食品经营许可证图片添加失败");
+    }
+
+
+
+    @ApiOperation("根据商户ID获取经营许可证历史记录")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "商户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/queryLicenceByStatusList")
+    public R<List<StoreLicenseHistoryDto>> queryLicenceByStatusList(@RequestParam("storeId") Integer storeId) {
+        log.info("LicenseController.queryLicenceByStatusList: storeId={}", storeId);
+        try {
+            List<StoreLicenseHistoryDto> result = licenseService.queryLicenceByStatusList(storeId);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("LicenseController.queryLicenceByStatusList ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "门店装修-续签合同")
+    @PostMapping("/uploadRenewalContract")
+    public R<String> uploadRenewalContract(@RequestBody List<StoreImg> storeImgList) {
+        log.info("StoreInfoController.uploadRenewalContract?storeImgList={}", storeImgList);
+        int num = licenseService.uploadRenewalContract(storeImgList);
+        if (num <= 0) {
+            return R.fail("续签合同图片添加失败");
+        }
+        return R.success("续签合同图片添加成功");
+    }
+
 }
 
 

+ 59 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/MerchantUserController.java

@@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreUser;
 import shop.alien.entity.store.vo.StoreUserVo;
+import shop.alien.storeplatform.dto.AddAlipayAccountDTO;
+import shop.alien.storeplatform.dto.SetPayPasswordDTO;
 import shop.alien.storeplatform.service.MerchantUserService;
 
 import java.util.Map;
@@ -85,6 +87,63 @@ public class MerchantUserController {
             return R.fail("查询失败:" + e.getMessage());
         }
     }
+
+    @ApiOperation("设置/修改支付密码")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/setPayPassword")
+    public R<Boolean> setPayPassword(@RequestBody SetPayPasswordDTO dto) {
+        log.info("MerchantUserController.setPayPassword - 设置支付密码");
+        try {
+            // 从token中获取当前登录用户的ID
+            Integer userId = shop.alien.storeplatform.util.LoginUserUtil.getCurrentUserId();
+            
+            boolean success = merchantUserService.setPayPassword(userId, dto.getPayPassword());
+            if (success) {
+                return R.success("设置成功");
+            }
+            return R.fail("设置失败");
+        } catch (Exception e) {
+            log.error("MerchantUserController.setPayPassword ERROR: {}", e.getMessage(), e);
+            return R.fail("设置失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("添加/修改支付宝账号")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/addAlipayAccount")
+    public R<Integer> addAlipayAccount(@RequestBody AddAlipayAccountDTO dto) {
+        log.info("MerchantUserController.addAlipayAccount - 添加支付宝账号");
+        try {
+            // 从token中获取当前登录用户的ID
+            Integer userId = shop.alien.storeplatform.util.LoginUserUtil.getCurrentUserId();
+            
+            Integer result = merchantUserService.addAlipayAccount(userId, dto.getAlipayAccount());
+            if (result > 0) {
+                return R.data(result, "添加成功");
+            }
+            return R.fail("添加失败");
+        } catch (Exception e) {
+            log.error("MerchantUserController.addAlipayAccount ERROR: {}", e.getMessage(), e);
+            return R.fail("添加失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("重置商户到刚注册状态")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/resetToInitialStatus")
+    public R<Boolean> resetToInitialStatus() {
+        log.info("MerchantUserController.resetToInitialStatus - 重置商户状态");
+        try {
+            boolean success = merchantUserService.resetToInitialStatus();
+            if (success) {
+                return R.data(true, "重置成功,已退回到刚注册状态");
+            }
+            return R.fail("重置失败");
+        } catch (Exception e) {
+            log.error("MerchantUserController.resetToInitialStatus ERROR: {}", e.getMessage(), e);
+            return R.fail("重置失败:" + e.getMessage());
+        }
+    }
 }
 
 

+ 1 - 1
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/NoticeController.java

@@ -74,7 +74,7 @@ public class NoticeController {
     @ApiImplicitParams({
             @ApiImplicitParam(name = "id", value = "通知ID", dataType = "Integer", paramType = "query", required = true)
     })
-    @PostMapping("/markNoticeAsRead")
+    @GetMapping("/markNoticeAsRead")
     public R<Boolean> markNoticeAsRead(@RequestParam("id") Integer id) {
         log.info("NoticeController.markNoticeAsRead?id={}", id);
         try {

+ 0 - 171
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StoreLicenseHistoryController.java

@@ -1,171 +0,0 @@
-package shop.alien.storeplatform.controller;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.swagger.annotations.*;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-import shop.alien.entity.result.R;
-import shop.alien.entity.store.StoreLicenseHistory;
-import shop.alien.entity.store.dto.StoreLicenseHistoryDTO;
-import shop.alien.entity.store.vo.StoreLicenseHistoryVO;
-import shop.alien.storeplatform.service.StoreLicenseHistoryService;
-
-import java.util.List;
-
-/**
- * 商户证照历史记录 Controller
- *
- * @author system
- * @since 2025-11-24
- */
-@Slf4j
-@Api(tags = {"商家端-证照历史管理"})
-@ApiSort(5)
-@CrossOrigin
-@RestController
-@RequestMapping("/storeLicenseHistory")
-@RequiredArgsConstructor
-public class StoreLicenseHistoryController {
-
-    private final StoreLicenseHistoryService storeLicenseHistoryService;
-
-    @ApiOperation("创建证照历史记录")
-    @ApiOperationSupport(order = 1)
-    @PostMapping("/create")
-    public R<StoreLicenseHistory> createLicenseHistory(@RequestBody @Validated StoreLicenseHistoryDTO dto) {
-        log.info("StoreLicenseHistoryController.createLicenseHistory: dto={}", dto);
-        try {
-            StoreLicenseHistory result = storeLicenseHistoryService.createLicenseHistory(dto);
-            return R.data(result, "创建成功");
-        } catch (Exception e) {
-            log.error("StoreLicenseHistoryController.createLicenseHistory ERROR: {}", e.getMessage(), e);
-            return R.fail(e.getMessage());
-        }
-    }
-
-    @ApiOperation("更新证照历史记录")
-    @ApiOperationSupport(order = 2)
-    @PutMapping("/update")
-    public R<Boolean> updateLicenseHistory(@RequestBody @Validated StoreLicenseHistoryDTO dto) {
-        log.info("StoreLicenseHistoryController.updateLicenseHistory: dto={}", dto);
-        try {
-            boolean result = storeLicenseHistoryService.updateLicenseHistory(dto);
-            if (result) {
-                return R.data(true, "更新成功");
-            }
-            return R.fail("更新失败");
-        } catch (Exception e) {
-            log.error("StoreLicenseHistoryController.updateLicenseHistory ERROR: {}", e.getMessage(), e);
-            return R.fail(e.getMessage());
-        }
-    }
-
-    @ApiOperation("删除证照历史记录")
-    @ApiOperationSupport(order = 3)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "id", value = "记录ID", dataType = "Integer", paramType = "query", required = true)
-    })
-    @DeleteMapping("/delete")
-    public R<Boolean> deleteLicenseHistory(@RequestParam("id") Integer id) {
-        log.info("StoreLicenseHistoryController.deleteLicenseHistory: id={}", id);
-        try {
-            boolean result = storeLicenseHistoryService.deleteLicenseHistory(id);
-            if (result) {
-                return R.data(true, "删除成功");
-            }
-            return R.fail("删除失败");
-        } catch (Exception e) {
-            log.error("StoreLicenseHistoryController.deleteLicenseHistory ERROR: {}", e.getMessage(), e);
-            return R.fail(e.getMessage());
-        }
-    }
-
-    @ApiOperation("根据ID获取证照历史记录详情")
-    @ApiOperationSupport(order = 4)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "id", value = "记录ID", dataType = "Integer", paramType = "query", required = true)
-    })
-    @GetMapping("/getById")
-    public R<StoreLicenseHistoryVO> getLicenseHistoryById(@RequestParam("id") Integer id) {
-        log.info("StoreLicenseHistoryController.getLicenseHistoryById: id={}", id);
-        try {
-            StoreLicenseHistoryVO result = storeLicenseHistoryService.getLicenseHistoryById(id);
-            if (result != null) {
-                return R.data(result);
-            }
-            return R.fail("记录不存在");
-        } catch (Exception e) {
-            log.error("StoreLicenseHistoryController.getLicenseHistoryById ERROR: {}", e.getMessage(), e);
-            return R.fail(e.getMessage());
-        }
-    }
-
-    @ApiOperation("根据商户ID获取证照历史记录列表")
-    @ApiOperationSupport(order = 5)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "storeId", value = "商户ID", dataType = "Integer", paramType = "query", required = true)
-    })
-    @GetMapping("/listByStoreId")
-    public R<List<StoreLicenseHistoryVO>> getLicenseHistoryListByStoreId(@RequestParam("storeId") Integer storeId) {
-        log.info("StoreLicenseHistoryController.getLicenseHistoryListByStoreId: storeId={}", storeId);
-        try {
-            List<StoreLicenseHistoryVO> result = storeLicenseHistoryService.getLicenseHistoryListByStoreId(storeId);
-            return R.data(result);
-        } catch (Exception e) {
-            log.error("StoreLicenseHistoryController.getLicenseHistoryListByStoreId ERROR: {}", e.getMessage(), e);
-            return R.fail(e.getMessage());
-        }
-    }
-
-    @ApiOperation("根据商户ID和证照类型获取证照历史记录列表")
-    @ApiOperationSupport(order = 6)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "storeId", value = "商户ID", dataType = "Integer", paramType = "query", required = true),
-            @ApiImplicitParam(name = "licenseStatus", value = "证照类型: 1-合同管理, 2-食品经营许可证", dataType = "Integer", paramType = "query", required = true)
-    })
-    @GetMapping("/listByStoreIdAndType")
-    public R<List<StoreLicenseHistoryVO>> getLicenseHistoryListByStoreIdAndType(
-            @RequestParam("storeId") Integer storeId,
-            @RequestParam("licenseStatus") Integer licenseStatus) {
-        log.info("StoreLicenseHistoryController.getLicenseHistoryListByStoreIdAndType: storeId={}, licenseStatus={}", 
-                 storeId, licenseStatus);
-        try {
-            List<StoreLicenseHistoryVO> result = storeLicenseHistoryService.getLicenseHistoryListByStoreIdAndType(storeId, licenseStatus);
-            return R.data(result);
-        } catch (Exception e) {
-            log.error("StoreLicenseHistoryController.getLicenseHistoryListByStoreIdAndType ERROR: {}", e.getMessage(), e);
-            return R.fail(e.getMessage());
-        }
-    }
-
-    @ApiOperation("分页查询证照历史记录")
-    @ApiOperationSupport(order = 7)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "current", value = "当前页", dataType = "Long", paramType = "query", required = true, defaultValue = "1"),
-            @ApiImplicitParam(name = "size", value = "每页大小", dataType = "Long", paramType = "query", required = true, defaultValue = "10"),
-            @ApiImplicitParam(name = "storeId", value = "商户ID(可选)", dataType = "Integer", paramType = "query", required = false),
-            @ApiImplicitParam(name = "licenseStatus", value = "证照类型(可选): 1-合同管理, 2-食品经营许可证", dataType = "Integer", paramType = "query", required = false),
-            @ApiImplicitParam(name = "licenseExecuteStatus", value = "审核状态(可选): 1-审核中, 2-审核拒绝, 3-审核通过", dataType = "Integer", paramType = "query", required = false)
-    })
-    @GetMapping("/page")
-    public R<Page<StoreLicenseHistoryVO>> getLicenseHistoryPage(
-            @RequestParam(value = "current", defaultValue = "1") Long current,
-            @RequestParam(value = "size", defaultValue = "10") Long size,
-            @RequestParam(value = "storeId", required = false) Integer storeId,
-            @RequestParam(value = "licenseStatus", required = false) Integer licenseStatus,
-            @RequestParam(value = "licenseExecuteStatus", required = false) Integer licenseExecuteStatus) {
-        log.info("StoreLicenseHistoryController.getLicenseHistoryPage: current={}, size={}, storeId={}, licenseStatus={}, licenseExecuteStatus={}", 
-                 current, size, storeId, licenseStatus, licenseExecuteStatus);
-        try {
-            Page<StoreLicenseHistory> page = new Page<>(current, size);
-            Page<StoreLicenseHistoryVO> result = storeLicenseHistoryService.getLicenseHistoryPage(page, storeId, licenseStatus, licenseExecuteStatus);
-            return R.data(result);
-        } catch (Exception e) {
-            log.error("StoreLicenseHistoryController.getLicenseHistoryPage ERROR: {}", e.getMessage(), e);
-            return R.fail(e.getMessage());
-        }
-    }
-}
-

+ 25 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/dto/AddAlipayAccountDTO.java

@@ -0,0 +1,25 @@
+package shop.alien.storeplatform.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 添加支付宝账号DTO
+ *
+ * @author ssk
+ * @since 2025-11-20
+ */
+@Data
+@ApiModel("添加支付宝账号DTO")
+public class AddAlipayAccountDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "支付宝账号(邮箱或手机号)", required = true, example = "alipay@example.com")
+    private String alipayAccount;
+}
+
+

+ 3 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/dto/MarkAllNoticesReadDTO.java

@@ -27,3 +27,6 @@ public class MarkAllNoticesReadDTO implements Serializable {
 
 
 
+
+
+

+ 25 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/dto/SetPayPasswordDTO.java

@@ -0,0 +1,25 @@
+package shop.alien.storeplatform.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 设置支付密码DTO
+ *
+ * @author ssk
+ * @since 2025-11-20
+ */
+@Data
+@ApiModel("设置支付密码DTO")
+public class SetPayPasswordDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "支付密码(6位数字)", required = true, example = "123456")
+    private String payPassword;
+}
+
+

+ 3 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/example/LoginUserUtilExample.java

@@ -255,3 +255,6 @@ public class LoginUserUtilExample {
 
 
 
+
+
+

+ 1 - 2
alien-store-platform/src/main/java/shop/alien/storeplatform/service/CouponManageService.java

@@ -23,10 +23,9 @@ public interface CouponManageService {
     /**
      * 核销订单
      *
-     * @param storeId  门店ID
      * @param quanCode 券码
      * @return 核销结果
      */
-    Map<String, String> verifyCoupon(String storeId, String quanCode);
+    Map<String, String> verifyCoupon(String quanCode);
 }
 

+ 110 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/IncomeManageService.java

@@ -0,0 +1,110 @@
+package shop.alien.storeplatform.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import shop.alien.entity.result.R;
+
+import java.util.Map;
+
+/**
+ * web端商户收入管理服务接口
+ *
+ * @author ssk
+ * @since 2025-11-14
+ */
+public interface IncomeManageService {
+
+    /**
+     * 账期查询
+     *
+     * @param storeId     门店ID
+     * @param incomeType  收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
+     * @param paymentType 账期类型, 0:未到, 1:已到
+     * @param startTime   开始时间
+     * @param endTime     结束时间
+     * @param page        页码
+     * @param size        每页条数
+     * @return 账期数据
+     */
+    JSONObject getPaymentCycle(Integer storeId, Integer incomeType, Integer paymentType,
+                               String startTime, String endTime, int page, int size);
+
+    /**
+     * 查询账户余额
+     *
+     * @param storeId 门店ID
+     * @return 账户余额信息(balance: 总余额, cashOutMoney: 可提现金额)
+     */
+    Map<String, Object> getAccountBalance(Integer storeId);
+
+    /**
+     * 提现申请
+     *
+     * @param storeId         门店ID
+     * @param payPassword     支付密码
+     * @param withdrawalMoney 提现金额(单位:分)
+     * @return 提现结果
+     */
+    R<?> cashOut(Integer storeId, String payPassword, Integer withdrawalMoney);
+
+    /**
+     * 提现记录查询
+     *
+     * @param storeId          门店ID
+     * @param cashOutStartTime 开始时间(格式:yyyy-MM-dd)
+     * @param cashOutEndTime   结束时间(格式:yyyy-MM-dd)
+     * @param paymentStatus    提现状态
+     * @param page             页码
+     * @param size             每页条数
+     * @return 提现记录列表
+     */
+    Object getCashOutRecordList(Integer storeId, String cashOutStartTime, String cashOutEndTime,
+                                String paymentStatus, Integer page, Integer size);
+
+    /**
+     * 团购收益查询
+     *
+     * @param storeId    门店ID
+     * @param date       日期(格式:yyyy-MM-dd)
+     * @param incomeType 收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
+     * @param page       页码
+     * @param size       每页条数
+     * @return 团购收益信息
+     */
+    Object getGroupIncome(Integer storeId, String date, Integer incomeType, Integer page, Integer size);
+
+    /**
+     * 快速提现申请(免审核,直接通过)
+     *
+     * @param storeId         门店ID
+     * @param payPassword     支付密码
+     * @param withdrawalMoney 提现金额(单位:分)
+     * @return 提现结果
+     */
+    R<?> applyFastCashOut(Integer storeId, String payPassword, Integer withdrawalMoney);
+
+    /**
+     * 已入账/未入账详情查询
+     *
+     * @param storeId     门店ID
+     * @param incomeType  收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
+     * @param paymentType 账期类型,0:未入账,1:已入账
+     * @param startTime   开始时间(格式:yyyy-MM-dd)
+     * @param endTime     结束时间(格式:yyyy-MM-dd)
+     * @param page        页数
+     * @param size        条数
+     * @return 入账详情
+     */
+    JSONObject getPaymentDetails(Integer storeId, Integer incomeType, Integer paymentType, 
+                                 String startTime, String endTime, int page, int size);
+
+    /**
+     * 收入汇总统计
+     * 包含:可提现金额、今日收益、已入账总数、未入账总数
+     *
+     * @param storeId 门店ID
+     * @return 收入汇总统计数据
+     */
+    Map<String, Object> getIncomeSummary(Integer storeId);
+}
+
+

+ 18 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/LicenseService.java

@@ -2,16 +2,34 @@ package shop.alien.storeplatform.service;
 
 
 import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryDto;
+import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
 
 import java.util.List;
 
 public interface LicenseService {
 
+    int uploadfoodLicence(StoreImg storeImg);
+
     List<StoreImg> getbusinessLicenseList (Integer id);
 
     List<StoreImg> getContractList (Integer id);
 
     List<StoreImg> getFoodLicenceList (Integer id);
+
+    /**
+     * 根据商户ID查询证照历史记录列表(带格式化日期)
+     *
+     * @param storeId 商户ID
+     * @return 证照历史记录VO列表
+     */
+    List<StoreLicenseHistoryDto> queryLicenceByStatusList (Integer storeId);
+
+    /**
+     * 店铺续签合同
+     *
+     */
+    int uploadRenewalContract(List<StoreImg> storeImgList);
 }
 
 

+ 26 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/MerchantUserService.java

@@ -37,6 +37,32 @@ public interface MerchantUserService {
      * @return 包含验证结果的Map
      */
     Map<String, Object> checkPayPassword(String storeUserId, String password);
+
+    /**
+     * 设置/修改支付密码
+     *
+     * @param id          用户ID
+     * @param payPassword 支付密码
+     * @return 是否设置成功
+     */
+    boolean setPayPassword(Integer id, String payPassword);
+
+    /**
+     * 添加/修改支付宝账号
+     *
+     * @param userId         用户ID
+     * @param alipayAccount  支付宝账号
+     * @return 影响的行数
+     */
+    Integer addAlipayAccount(Integer userId, String alipayAccount);
+
+    /**
+     * 重置商户到刚注册状态
+     * (一键退回:删除店铺申请、解绑店铺、清理相关数据)
+     *
+     * @return 是否重置成功
+     */
+    boolean resetToInitialStatus();
 }
 
 

+ 0 - 79
alien-store-platform/src/main/java/shop/alien/storeplatform/service/StoreLicenseHistoryService.java

@@ -1,79 +0,0 @@
-package shop.alien.storeplatform.service;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import shop.alien.entity.store.StoreLicenseHistory;
-import shop.alien.entity.store.dto.StoreLicenseHistoryDTO;
-import shop.alien.entity.store.vo.StoreLicenseHistoryVO;
-
-import java.util.List;
-
-/**
- * 商户证照历史记录 Service 接口
- *
- * @author system
- * @since 2025-11-24
- */
-public interface StoreLicenseHistoryService {
-
-    /**
-     * 创建证照历史记录
-     *
-     * @param dto 证照历史记录DTO
-     * @return 创建结果
-     */
-    StoreLicenseHistory createLicenseHistory(StoreLicenseHistoryDTO dto);
-
-    /**
-     * 更新证照历史记录
-     *
-     * @param dto 证照历史记录DTO
-     * @return 更新结果
-     */
-    boolean updateLicenseHistory(StoreLicenseHistoryDTO dto);
-
-    /**
-     * 根据ID删除证照历史记录
-     *
-     * @param id 记录ID
-     * @return 删除结果
-     */
-    boolean deleteLicenseHistory(Integer id);
-
-    /**
-     * 根据ID获取证照历史记录详情
-     *
-     * @param id 记录ID
-     * @return 证照历史记录详情
-     */
-    StoreLicenseHistoryVO getLicenseHistoryById(Integer id);
-
-    /**
-     * 根据商户ID获取证照历史记录列表
-     *
-     * @param storeId 商户ID
-     * @return 证照历史记录列表
-     */
-    List<StoreLicenseHistoryVO> getLicenseHistoryListByStoreId(Integer storeId);
-
-    /**
-     * 根据商户ID和证照类型获取证照历史记录列表
-     *
-     * @param storeId       商户ID
-     * @param licenseStatus 证照类型
-     * @return 证照历史记录列表
-     */
-    List<StoreLicenseHistoryVO> getLicenseHistoryListByStoreIdAndType(Integer storeId, Integer licenseStatus);
-
-    /**
-     * 分页查询证照历史记录
-     *
-     * @param page          分页对象
-     * @param storeId       商户ID(可选)
-     * @param licenseStatus 证照类型(可选)
-     * @param licenseExecuteStatus 审核状态(可选)
-     * @return 分页结果
-     */
-    Page<StoreLicenseHistoryVO> getLicenseHistoryPage(Page<StoreLicenseHistory> page, Integer storeId, 
-                                                       Integer licenseStatus, Integer licenseExecuteStatus);
-}
-

+ 6 - 4
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/CouponManageServiceImpl.java

@@ -13,6 +13,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.*;
 import shop.alien.mapper.*;
 import shop.alien.storeplatform.service.CouponManageService;
+import shop.alien.storeplatform.util.LoginUserUtil;
 import shop.alien.util.common.constant.OrderStatusEnum;
 
 import java.math.BigDecimal;
@@ -531,14 +532,15 @@ public class CouponManageServiceImpl implements CouponManageService {
     /**
      * 核销订单
      *
-     * @param storeId  门店ID
      * @param quanCode 券码
      * @return 核销结果
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public Map<String, String> verifyCoupon(String storeId, String quanCode) {
-        log.info("CouponManageServiceImpl.verifyCoupon - 开始核销订单: storeId={}, quanCode={}", storeId, quanCode);
+    public Map<String, String> verifyCoupon(String quanCode) {
+        log.info("CouponManageServiceImpl.verifyCoupon - 开始核销订单: quanCode={}", quanCode);
+        // 获取登录信息
+        Integer storeId = LoginUserUtil.getCurrentStoreId();
 
         Map<String, String> resultMap = new HashMap<>();
         String lockKey = "coupon:use:" + quanCode;
@@ -674,7 +676,7 @@ public class CouponManageServiceImpl implements CouponManageService {
 
             // 11. 插入收入明细记录
             StoreIncomeDetailsRecord record = new StoreIncomeDetailsRecord();
-            record.setStoreId(Integer.parseInt(storeId));
+            record.setStoreId(storeId);
             record.setUserOrderId(orderCouponMiddle.getId());
             record.setIncomeType(lifeUserOrder.getCouponType());
             record.setBusinessId(orderCouponMiddle.getCouponId());

+ 1005 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/IncomeManageServiceImpl.java

@@ -0,0 +1,1005 @@
+package shop.alien.storeplatform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreCashOutRecord;
+import shop.alien.entity.store.StoreIncomeDetailsRecord;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.StoreCashOutRecordVo;
+import shop.alien.entity.store.vo.StoreIncomeDetailsRecordVo;
+import shop.alien.mapper.StoreCashOutRecordMapper;
+import shop.alien.mapper.StoreIncomeDetailsRecordMapper;
+import shop.alien.mapper.StoreInfoMapper;
+import shop.alien.mapper.StoreUserMapper;
+import shop.alien.storeplatform.service.IncomeManageService;
+import shop.alien.util.common.ListToPage;
+import shop.alien.util.common.constant.CouponTypeEnum;
+import shop.alien.util.date.DateUtils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * web端商户收入管理服务实现
+ *
+ * @author ssk
+ * @since 2025-11-14
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class IncomeManageServiceImpl extends ServiceImpl<StoreIncomeDetailsRecordMapper, StoreIncomeDetailsRecord> implements IncomeManageService {
+
+    private final StoreIncomeDetailsRecordMapper storeIncomeDetailsRecordMapper;
+    private final StoreInfoMapper storeInfoMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final StoreCashOutRecordMapper storeCashOutRecordMapper;
+
+    @Override
+    public com.alibaba.fastjson2.JSONObject getPaymentCycle(Integer storeId, Integer incomeType, Integer paymentType,
+                                      String startTime, String endTime, int page, int size) {
+        log.info("IncomeManageServiceImpl.getPaymentCycle - 开始查询账期: storeId={}, incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
+                storeId, incomeType, paymentType, startTime, endTime, page, size);
+
+        QueryWrapper<StoreIncomeDetailsRecord> wrapper = new QueryWrapper<>();
+        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+        // 1. 根据账期类型设置查询条件
+        if (paymentType != null && paymentType == 0) {
+            // 未到账期: 未绑定提现记录 & 当前时间-3天大于创建时间
+            wrapper.isNull("sidr.cash_out_id");
+            wrapper.gt("sidr.created_time", DateUtils.calcDays(new Date(), -3));
+            Date startDate = DateUtils.calcDays(now, -3);
+            jsonObject.put("date", df.format(startDate) + " ~ " + df.format(now));
+            log.debug("IncomeManageServiceImpl.getPaymentCycle - 未到账期: date={}", jsonObject.get("date"));
+        } else if (paymentType != null && paymentType == 1) {
+            // 已到账期: 已绑定提现记录 & 当前时间-4~27天大于创建时间
+//            wrapper.isNotNull("sidr.cash_out_id");
+            wrapper.between("sidr.created_time", DateUtils.calcDays(new Date(), -27), DateUtils.calcDays(new Date(), -4));
+            Date startDate = DateUtils.calcDays(now, -27);
+            Date endDate = DateUtils.calcDays(now, -4);
+            jsonObject.put("date", df.format(startDate) + " ~ " + df.format(endDate));
+            log.debug("IncomeManageServiceImpl.getPaymentCycle - 已到账期: date={}", jsonObject.get("date"));
+        }
+
+        // 2. 门店ID条件
+        if (storeId != null) {
+            wrapper.eq("sidr.store_id", storeId);
+        }
+
+        // 3. 时间范围条件(用户选择的时间范围)
+        if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
+            LocalDate startDate = LocalDate.parse(startTime);
+            LocalDate endDate = LocalDate.parse(endTime);
+            LocalDateTime startOfDay = startDate.atStartOfDay();
+            LocalDateTime endOfDay = endDate.atTime(LocalTime.MAX);
+            wrapper.between("sidr.created_time", startOfDay, endOfDay)
+                    .orderByDesc("sidr.created_time");
+        }
+
+        // 4. 收入类型条件
+        if (null != incomeType) {
+            if (0 == incomeType) {
+                // 主页: 包含优惠券和代金券
+                wrapper.in("sidr.income_type", CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
+                log.debug("IncomeManageServiceImpl.getPaymentCycle - 收入类型: 主页(优惠券+代金券)");
+            } else {
+                wrapper.eq("sidr.income_type", incomeType);
+                log.debug("IncomeManageServiceImpl.getPaymentCycle - 收入类型: {}", incomeType);
+            }
+        }
+
+        // 5. 查询收入明细记录列表
+        List<StoreIncomeDetailsRecordVo> list = storeIncomeDetailsRecordMapper.selectRecordList(wrapper);
+        log.info("IncomeManageServiceImpl.getPaymentCycle - 查询到收入记录数: {}", list.size());
+
+        // 6. 查询门店信息(用于获取抽成比例)
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+        if (storeInfo == null) {
+            log.warn("IncomeManageServiceImpl.getPaymentCycle - 门店不存在: storeId={}", storeId);
+            throw new RuntimeException("门店不存在");
+        }
+
+        // 7. 处理列表数据:格式化金额、日期
+        for (StoreIncomeDetailsRecordVo storeIncomeDetailsRecord : list) {
+            if (storeIncomeDetailsRecord == null) {
+                continue;
+            }
+            
+            StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
+            BeanUtils.copyProperties(storeIncomeDetailsRecord, vo);
+            
+            // 将金额从分转换为元(保留2位小数)
+            storeIncomeDetailsRecord.setMoneyStr(
+                    new BigDecimal(vo.getMoney())
+                            .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                            .toString()
+            );
+            
+            // 格式化日期
+            String format = df.format(storeIncomeDetailsRecord.getCreatedTime());
+            storeIncomeDetailsRecord.setDate(format);
+            
+            // 设置抽成比例
+            vo.setCommissionRate(storeInfo.getCommissionRate());
+        }
+
+        // 8. 手动分页
+        jsonObject.put("data", ListToPage.setPage(list, page, size));
+
+        // 9. 计算总金额(元)
+        int totalMoney = list.stream().mapToInt(StoreIncomeDetailsRecord::getMoney).sum();
+        jsonObject.put("money",
+                new BigDecimal(totalMoney)
+                        .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                        .toString()
+        );
+
+        log.info("IncomeManageServiceImpl.getPaymentCycle - 查询完成: 总记录数={}, 总金额={}元",
+                list.size(), jsonObject.get("money"));
+
+        return jsonObject;
+    }
+
+    @Override
+    public Map<String, Object> getAccountBalance(Integer storeId) {
+        log.info("IncomeManageServiceImpl.getAccountBalance - 开始查询账户余额: storeId={}", storeId);
+
+        Map<String, Object> map = new HashMap<>();
+
+        // 1. 查询店铺用户信息,获取账户总余额
+        LambdaQueryWrapper<StoreUser> storeUserWrapper = new LambdaQueryWrapper<>();
+        storeUserWrapper.eq(StoreUser::getStoreId, storeId);
+        StoreUser storeUser = storeUserMapper.selectOne(storeUserWrapper);
+
+        if (storeUser == null) {
+            log.warn("IncomeManageServiceImpl.getAccountBalance - 店铺用户不存在: storeId={}", storeId);
+            throw new RuntimeException("店铺用户不存在");
+        }
+
+        // 2. 账户总余额(单位:元,保留2位小数)
+        String balance = new BigDecimal(storeUser.getMoney())
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+        map.put("balance", balance);
+        log.debug("IncomeManageServiceImpl.getAccountBalance - 账户总余额: {}元", balance);
+
+        // 3. 查询可提现金额(4~27天内的收入,且未绑定提现记录)
+        LambdaQueryWrapper<StoreIncomeDetailsRecord> wrapper = new LambdaQueryWrapper<>();
+        wrapper.between(StoreIncomeDetailsRecord::getCreatedTime,
+                        DateUtils.calcDays(new Date(), -27),
+                        DateUtils.calcDays(new Date(), -4))
+                .isNull(StoreIncomeDetailsRecord::getCashOutId)  // 未绑定提现记录
+                .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
+
+        List<StoreIncomeDetailsRecord> incomeList = storeIncomeDetailsRecordMapper.selectList(wrapper);
+        
+        // 计算可提现收入总额(分)
+        int cashOutMoney = incomeList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+        
+        log.debug("IncomeManageServiceImpl.getAccountBalance - 4~27天内收入总额: {}分", cashOutMoney);
+
+        // 4. 查询已提现和待审核的金额(需要扣除)
+        // payment_status: 1-待审核, 3-已提现
+        QueryWrapper<StoreCashOutRecord> cashOutWrapper = new QueryWrapper<>();
+        cashOutWrapper.eq("store_id", storeId)
+                .in("payment_status", "1", "3")
+                .eq("delete_flag", "0");
+        
+        List<StoreCashOutRecord> storeCashOutRecords = storeCashOutRecordMapper.selectList(cashOutWrapper);
+        
+        // 计算已提现和待审核的总金额(分)
+        int totalCashOutAmount = storeCashOutRecords.stream()
+                .collect(Collectors.summingInt(StoreCashOutRecord::getMoney));
+        
+        log.debug("IncomeManageServiceImpl.getAccountBalance - 已提现+待审核金额: {}分", totalCashOutAmount);
+
+        // 5. 计算最终可提现金额 = 可提现收入 - (已提现 + 待审核)
+        BigDecimal finalCashOutMoney = new BigDecimal(cashOutMoney)
+                .subtract(BigDecimal.valueOf(totalCashOutAmount))
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+        
+        map.put("cashOutMoney", finalCashOutMoney.toString());
+
+        log.info("IncomeManageServiceImpl.getAccountBalance - 查询完成: 总余额={}元, 可提现金额={}元",
+                balance, finalCashOutMoney);
+
+        return map;
+    }
+
+    /**
+     * 提现申请
+     * 
+     * @param storeId         门店ID
+     * @param payPassword     支付密码
+     * @param withdrawalMoney 提现金额(单位:分)
+     * @return 提现结果
+     */
+    @Transactional
+    @Override
+    public R<?> cashOut(Integer storeId, String payPassword, Integer withdrawalMoney) {
+        log.info("IncomeManageServiceImpl.cashOut - 开始处理提现: storeId={}, withdrawalMoney={}分",
+                storeId, withdrawalMoney);
+
+        // 1. 验证支付密码并查询用户信息
+        LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
+        userWrapper.eq(StoreUser::getStoreId, storeId)
+                .eq(StoreUser::getPayPassword, payPassword);
+        StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
+
+        if (storeUser == null) {
+            log.warn("IncomeManageServiceImpl.cashOut - 支付密码错误: storeId={}", storeId);
+            return R.fail("支付密码错误");
+        }
+
+        // 2. 验证账户余额是否充足
+        if (storeUser.getMoney() < withdrawalMoney) {
+            log.warn("IncomeManageServiceImpl.cashOut - 余额不足: 账户余额={}分, 提现金额={}分",
+                    storeUser.getMoney(), withdrawalMoney);
+            return R.fail("余额不足");
+        }
+
+        // 3. 验证提现金额是否满足最低要求(0.1元 = 10分)
+        BigDecimal amountInYuan = new BigDecimal(withdrawalMoney)
+                .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
+        if (amountInYuan.compareTo(new BigDecimal("0.1")) < 0) {
+            log.warn("IncomeManageServiceImpl.cashOut - 提现金额过小: {}元", amountInYuan);
+            return R.fail("金额不能小于0.1元");
+        }
+
+        log.info("IncomeManageServiceImpl.cashOut - 验证通过: 账户余额={}分, 提现金额={}元",
+                storeUser.getMoney(), amountInYuan);
+
+        // 4. 调用支付宝转账
+        // 注意:这里需要集成支付宝API,目前先创建提现申请记录
+        // 实际生产环境需要调用 AliApi.pay() 或 AliApi.payAccount()
+        
+        try {
+            // TODO: 调用支付宝转账API
+            // StoreAliPayLog pay;
+            // if (StringUtils.isNotBlank(storeUser.getAlipayAccount())) {
+            //     pay = aliApi.pay(storeUser.getName(), storeUser.getIdCard(), 
+            //                      storeUser.getAlipayAccount(), amountInYuan.toString());
+            // } else {
+            //     pay = aliApi.payAccount(storeUser.getName(), storeUser.getIdCard(), 
+            //                            null, amountInYuan.toString(), storeUser.getPhone());
+            // }
+            
+            // 5. 创建提现记录(待审核状态)
+            // Web端采用人工审核模式,不直接调用支付宝接口
+            StoreCashOutRecord storeCashOutRecord = new StoreCashOutRecord();
+            storeCashOutRecord.setStoreId(storeId);
+            storeCashOutRecord.setMoney(withdrawalMoney);
+            storeCashOutRecord.setCashOutType(0);
+            storeCashOutRecord.setPaymentStatus(1);  // 1-待审核
+            storeCashOutRecord.setDeleteFlag(0);
+            storeCashOutRecord.setStoreUserId(storeUser.getId());
+            storeCashOutRecord.setPayDate(new Date());
+            
+            // 插入提现记录
+            int insertResult = storeCashOutRecordMapper.insert(storeCashOutRecord);
+            if (insertResult <= 0) {
+                log.error("IncomeManageServiceImpl.cashOut - 创建提现记录失败");
+                throw new RuntimeException("创建提现记录失败");
+            }
+
+            // 6. 扣减账户余额
+            StoreUser updateUser = new StoreUser();
+            updateUser.setId(storeUser.getId());
+            updateUser.setMoney(storeUser.getMoney() - withdrawalMoney);
+            int updateResult = storeUserMapper.updateById(updateUser);
+            if (updateResult <= 0) {
+                log.error("IncomeManageServiceImpl.cashOut - 更新账户余额失败");
+                throw new RuntimeException("更新账户余额失败");
+            }
+
+            log.info("IncomeManageServiceImpl.cashOut - 提现申请成功: 提现记录ID={}, 提现金额={}元",
+                    storeCashOutRecord.getId(), amountInYuan);
+
+            // 7. 返回结果
+            Map<String, Object> result = new HashMap<>();
+            result.put("cashOutRecordId", storeCashOutRecord.getId());
+            result.put("withdrawalMoney", amountInYuan.toString());
+            result.put("status", "pending");  // pending-待审核
+            result.put("message", "提现申请已提交,等待审核");
+            
+            return R.data(result);
+
+        } catch (Exception e) {
+            log.error("IncomeManageServiceImpl.cashOut - 提现失败: {}", e.getMessage(), e);
+            
+            // 如果是已知的运行时异常,直接抛出让事务回滚
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+            
+            // 其他异常也需要回滚
+            throw new RuntimeException("提现失败:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 提现记录查询
+     *
+     * @param storeId          门店ID
+     * @param cashOutStartTime 开始时间
+     * @param cashOutEndTime   结束时间
+     * @param paymentStatus    提现状态
+     * @param page             页码
+     * @param size             每页条数
+     * @return 提现记录列表
+     */
+    @Override
+    public Object getCashOutRecordList(Integer storeId, String cashOutStartTime, String cashOutEndTime,
+                                       String paymentStatus, Integer page, Integer size) {
+        log.info("IncomeManageServiceImpl.getCashOutRecordList - 开始查询提现记录: storeId={}, cashOutStartTime={}, cashOutEndTime={}, paymentStatus={}, page={}, size={}",
+                storeId, cashOutStartTime, cashOutEndTime, paymentStatus, page, size);
+
+        // 1. 构建查询条件(使用QueryWrapper和表别名scor,以配合selectCashoutRecordList的LEFT JOIN查询)
+        QueryWrapper<StoreCashOutRecord> wrapper = new QueryWrapper<>();
+        
+        // 必填条件:门店ID
+        wrapper.eq("scor.store_id", storeId);
+        
+        // 可选条件:提现状态
+        if (StringUtils.isNotBlank(paymentStatus)) {
+            wrapper.eq("scor.payment_status", paymentStatus);
+            log.debug("IncomeManageServiceImpl.getCashOutRecordList - 筛选提现状态: {}", paymentStatus);
+        }
+        
+        // 可选条件:结束时间
+        if (StringUtils.isNotBlank(cashOutEndTime)) {
+            wrapper.le("scor.created_time", cashOutEndTime + " 23:59:59");
+            log.debug("IncomeManageServiceImpl.getCashOutRecordList - 结束时间: {}", cashOutEndTime);
+        }
+        
+        // 可选条件:开始时间
+        if (StringUtils.isNotBlank(cashOutStartTime)) {
+            wrapper.ge("scor.created_time", cashOutStartTime + " 00:00:00");
+            log.debug("IncomeManageServiceImpl.getCashOutRecordList - 开始时间: {}", cashOutStartTime);
+        }
+        
+        // 删除标识(软删除)
+        wrapper.eq("scor.delete_flag", 0);
+        
+        // 按创建时间倒序排序
+        wrapper.orderByDesc("scor.created_time");
+
+        // 2. 查询所有符合条件的记录(LEFT JOIN store_user,获取结算账户信息)
+        List<StoreCashOutRecord> recordList = storeCashOutRecordMapper.selectCashoutRecordList(wrapper);
+        log.info("IncomeManageServiceImpl.getCashOutRecordList - 查询到提现记录数: {}", recordList.size());
+
+        // 3. 手动分页
+        IPage<StoreCashOutRecord> storeCashOutRecordIPage = ListToPage.setPage(recordList, page, size);
+
+        // 4. 构建返回VO
+        StoreCashOutRecordVo vo = new StoreCashOutRecordVo();
+        
+        // 分页后的记录列表
+        vo.setCashOutRecordList(storeCashOutRecordIPage.getRecords());
+        
+        // 提现总金额(仅统计状态为1-待审核的记录)
+        int totalMoney = recordList.stream()
+                .filter(item -> "1".equals(item.getPaymentStatus().toString()))
+                .mapToInt(StoreCashOutRecord::getMoney)
+                .sum();
+        BigDecimal cashOutAllMoney = new BigDecimal(totalMoney)
+                .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
+        vo.setCashOutAllMoney(cashOutAllMoney);
+        
+        // 提现记录总数
+        vo.setCashOutNum(recordList.size());
+
+        log.info("IncomeManageServiceImpl.getCashOutRecordList - 查询完成: 总记录数={}, 总金额={}元",
+                recordList.size(), cashOutAllMoney);
+
+        return vo;
+    }
+
+    /**
+     * 团购收益查询
+     *
+     * @param storeId    门店ID
+     * @param date       日期
+     * @param incomeType 收入类型
+     * @param page       页码
+     * @param size       每页条数
+     * @return 团购收益信息
+     */
+    @Override
+    public Object getGroupIncome(Integer storeId, String date, Integer incomeType, Integer page, Integer size) {
+        log.info("IncomeManageServiceImpl.getGroupIncome - 开始查询团购收益: storeId={}, date={}, incomeType={}, page={}, size={}",
+                storeId, date, incomeType, page, size);
+
+        // 1. 构建查询条件:指定日期内、未绑定提现记录
+        LambdaQueryWrapper<StoreIncomeDetailsRecord> wrapper = new LambdaQueryWrapper<>();
+        wrapper.between(StoreIncomeDetailsRecord::getCreatedTime, date + " 00:00:00", date + " 23:59:59")
+                .isNull(StoreIncomeDetailsRecord::getCashOutId)  // 未绑定提现记录
+                .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
+
+        // 2. 收入类型筛选
+        // 2025-11-17 前端展示逻辑将团购券去掉,仅展示优惠券(前端不传0)
+        if (null != incomeType && 0 == incomeType) {
+            wrapper.in(StoreIncomeDetailsRecord::getIncomeType,
+                    CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
+            log.debug("IncomeManageServiceImpl.getGroupIncome - 收入类型: 主页(优惠券+团购券)");
+        } else if (null != incomeType) {
+            wrapper.eq(StoreIncomeDetailsRecord::getIncomeType, incomeType);
+            log.debug("IncomeManageServiceImpl.getGroupIncome - 收入类型: {}", incomeType);
+        }
+
+        // 3. 查询店铺信息
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+        if (storeInfo == null) {
+            log.warn("IncomeManageServiceImpl.getGroupIncome - 门店不存在: storeId={}", storeId);
+            throw new RuntimeException("门店不存在");
+        }
+
+        // 4. 查询收入明细记录
+        List<StoreIncomeDetailsRecord> list = this.list(wrapper);
+        log.info("IncomeManageServiceImpl.getGroupIncome - 查询到收入记录数: {}", list.size());
+
+        // 5. 计算总收入和手续费
+        Integer allIncome = 0;
+        Integer commission = 0;
+        for (StoreIncomeDetailsRecord storeIncomeDetailsRecord : list) {
+            allIncome += storeIncomeDetailsRecord.getMoney();
+            commission += storeIncomeDetailsRecord.getCommission();
+        }
+
+        // 6. 构建返回VO
+        StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
+        vo.setCommissionRate(storeInfo.getCommissionRate());
+        vo.setDate(date);
+        vo.setStoreName(storeInfo.getStoreName());
+        vo.setStoreId(storeId);
+
+        // 7. 格式化金额信息
+        // 售价(收入+手续费)
+        vo.setIncomeMoney(
+                new BigDecimal(allIncome)
+                        .add(new BigDecimal(commission))
+                        .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                        .toString()
+        );
+
+        // 手续费
+        vo.setCommissionStr(
+                new BigDecimal(commission)
+                        .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                        .toString()
+        );
+
+        // 收益(售价-手续费,存入时已经减完了)
+        vo.setNoYetPaymentMoney(
+                new BigDecimal(allIncome)
+                        .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                        .toString()
+        );
+
+        // 8. 查询明细记录列表(含退款标识)
+        QueryWrapper<StoreIncomeDetailsRecordVo> incomeWrapper = new QueryWrapper<>();
+        incomeWrapper.eq("income.delete_flag", 0)
+                .eq("income.store_id", storeId)
+                .between("income.created_time", date + " 00:00:00", date + " 23:59:59")
+                .orderByDesc("income.created_time");
+
+        if (null != incomeType) {
+            if (0 == incomeType) {
+                incomeWrapper.in("income_type",
+                        CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
+            } else {
+                incomeWrapper.eq("income_type", incomeType);
+            }
+        }
+
+        List<StoreIncomeDetailsRecordVo> incomeDetailsRecordVoList =
+                storeIncomeDetailsRecordMapper.getIncomeList(incomeWrapper);
+
+        // 9. 按退款类型分组
+        Map<String, List<StoreIncomeDetailsRecordVo>> collect =
+                incomeDetailsRecordVoList.stream()
+                        .collect(Collectors.groupingBy(x -> x.getRefundType()));
+
+        // 10. 设置明细列表(不含退款)和统计信息
+        vo.setIncomeDetailsRecordVoList(new ArrayList<>());
+        vo.setRefundMoney(new BigDecimal(0));
+        vo.setCouponCount(0);
+
+        // 正常收入记录(未退款)
+        if (collect.containsKey("false")) {
+            vo.setIncomeDetailsRecordVoList(
+                    ListToPage.setPage(incomeDetailsRecordVoList, page, size).getRecords()
+            );
+            // 计算券数量(正常订单 - 退款订单)
+            vo.setCouponCount(
+                    collect.get("false").size() -
+                            (collect.containsKey("true") ? collect.get("true").size() : 0)
+            );
+        }
+
+        // 退款金额统计
+        if (collect.containsKey("true")) {
+            vo.setRefundMoney(
+                    new BigDecimal(
+                            collect.get("true").stream()
+                                    .mapToInt(StoreIncomeDetailsRecordVo::getMoney)
+                                    .sum()
+                    ).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+            );
+        }
+
+        log.info("IncomeManageServiceImpl.getGroupIncome - 查询完成: 总收入={}元, 手续费={}元, 券数量={}",
+                vo.getNoYetPaymentMoney(), vo.getCommissionStr(), vo.getCouponCount());
+
+        return vo;
+    }
+
+    /**
+     * 快速提现申请(免审核,直接通过状态)
+     * 与app端applyCashOut逻辑保持一致
+     *
+     * @param storeId         门店ID
+     * @param payPassword     支付密码
+     * @param withdrawalMoney 提现金额(单位:分)
+     * @return 提现结果
+     */
+    @Transactional
+    @Override
+    public R<?> applyFastCashOut(Integer storeId, String payPassword, Integer withdrawalMoney) {
+        log.info("IncomeManageServiceImpl.applyFastCashOut - 开始处理快速提现: storeId={}, withdrawalMoney={}分",
+                storeId, withdrawalMoney);
+
+        // 1. 验证支付密码并查询用户信息
+        LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
+        userWrapper.eq(StoreUser::getStoreId, storeId)
+                .eq(StoreUser::getPayPassword, payPassword);
+        StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
+
+        if (storeUser == null) {
+            log.warn("IncomeManageServiceImpl.applyFastCashOut - 支付密码错误: storeId={}", storeId);
+            return R.fail("支付密码错误");
+        }
+
+        // 2. 验证账户余额是否充足
+        if (storeUser.getMoney() <= withdrawalMoney) {
+            log.warn("IncomeManageServiceImpl.applyFastCashOut - 余额不足: 账户余额={}分, 提现金额={}分",
+                    storeUser.getMoney(), withdrawalMoney);
+            return R.fail("余额不足");
+        }
+
+        // 3. 验证提现金额是否满足最低要求(0.1元 = 10分)
+        BigDecimal amountInYuan = new BigDecimal(withdrawalMoney)
+                .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
+        if (amountInYuan.compareTo(new BigDecimal("0.1")) < 0) {
+            log.warn("IncomeManageServiceImpl.applyFastCashOut - 提现金额过小: {}元", amountInYuan);
+            return R.fail("金额不能小于0.1元");
+        }
+
+        log.info("IncomeManageServiceImpl.applyFastCashOut - 验证通过: 账户余额={}分, 提现金额={}元",
+                storeUser.getMoney(), amountInYuan);
+
+        try {
+            // 4. 创建提现记录(已通过状态,与app端保持一致)
+            StoreCashOutRecord storeCashOutRecord = new StoreCashOutRecord();
+            storeCashOutRecord.setStoreId(storeId);
+            storeCashOutRecord.setMoney(withdrawalMoney);
+            storeCashOutRecord.setCashOutType(0);  // 0-全部提现
+            storeCashOutRecord.setPaymentStatus(3);  // 3-已通过(与app端保持一致)
+            storeCashOutRecord.setDeleteFlag(0);
+            storeCashOutRecord.setIncomeStartTime(new Date());
+            storeCashOutRecord.setIncomeEndTime(new Date());
+            storeCashOutRecord.setStoreUserId(storeUser.getId());
+
+            // 5. 插入提现记录
+            int insertResult = storeCashOutRecordMapper.insert(storeCashOutRecord);
+            if (insertResult <= 0) {
+                log.error("IncomeManageServiceImpl.applyFastCashOut - 创建提现记录失败");
+                throw new RuntimeException("创建提现记录失败");
+            }
+
+            log.info("IncomeManageServiceImpl.applyFastCashOut - 快速提现申请成功: 提现记录ID={}, 提现金额={}元",
+                    storeCashOutRecord.getId(), amountInYuan);
+
+            // 6. 返回结果(返回StoreCashOutRecord对象,与app端保持一致)
+            return R.data(storeCashOutRecord);
+
+        } catch (Exception e) {
+            log.error("IncomeManageServiceImpl.applyFastCashOut - 快速提现失败: {}", e.getMessage(), e);
+
+            // 如果是已知的运行时异常,直接抛出让事务回滚
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+
+            // 其他异常也需要回滚
+            throw new RuntimeException("快速提现失败:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 已入账/未入账详情查询
+     *
+     * @param storeId     门店ID
+     * @param incomeType  收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
+     * @param paymentType 账期类型,0:未入账,1:已入账
+     * @param startTime   开始时间
+     * @param endTime     结束时间
+     * @param page        页数
+     * @param size        条数
+     * @return 入账详情
+     */
+    @Override
+    public com.alibaba.fastjson2.JSONObject getPaymentDetails(Integer storeId, Integer incomeType, Integer paymentType,
+                                                               String startTime, String endTime, int page, int size) {
+        log.info("IncomeManageServiceImpl.getPaymentDetails - 查询入账详情: storeId={}, incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
+                storeId, incomeType, paymentType, startTime, endTime, page, size);
+
+        QueryWrapper<StoreIncomeDetailsRecord> wrapper = new QueryWrapper<>();
+        com.alibaba.fastjson2.JSONObject result = new com.alibaba.fastjson2.JSONObject();
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+        // 根据 paymentType 设置时间范围和条件
+        if (paymentType == 0) {
+            // 未入账: 当前时间-3天内的记录
+            wrapper.isNull("sidr.cash_out_id");
+            wrapper.gt("sidr.created_time", DateUtils.calcDays(new Date(), -3));
+            Date startDate = DateUtils.calcDays(now, -3);
+            result.put("date", df.format(startDate) + " ~ " + df.format(now));
+            log.info("IncomeManageServiceImpl.getPaymentDetails - 未入账时间范围: {} ~ {}", df.format(startDate), df.format(now));
+        } else {
+            // 已入账: 当前时间-4~27天的记录
+            wrapper.between("sidr.created_time", DateUtils.calcDays(new Date(), -27), DateUtils.calcDays(new Date(), -4));
+            Date startDate = DateUtils.calcDays(now, -27);
+            Date endDate = DateUtils.calcDays(now, -4);
+            result.put("date", df.format(startDate) + " ~ " + df.format(endDate));
+            log.info("IncomeManageServiceImpl.getPaymentDetails - 已入账时间范围: {} ~ {}", df.format(startDate), df.format(endDate));
+        }
+
+        // 店铺ID条件
+        if (storeId != null) {
+            wrapper.eq("sidr.store_id", storeId);
+        }
+
+        // 自定义时间范围(用户输入的时间范围)
+        LocalDate startDate = LocalDate.parse(startTime);
+        LocalDate endDate = LocalDate.parse(endTime);
+        LocalDateTime startOfDay = startDate.atStartOfDay();
+        LocalDateTime endOfDay = endDate.atTime(LocalTime.MAX);
+        wrapper.between("sidr.created_time", startOfDay, endOfDay)
+                .orderByDesc("sidr.created_time");
+
+        // 收入类型条件
+        if (null != incomeType) {
+            if (0 == incomeType) {
+                // 主页: 包含优惠券和团购券
+                wrapper.in("sidr.income_type", CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
+            } else {
+                wrapper.eq("sidr.income_type", incomeType);
+            }
+        }
+
+        // 查询收入明细列表
+        List<StoreIncomeDetailsRecordVo> list = storeIncomeDetailsRecordMapper.selectRecordList(wrapper);
+        log.info("IncomeManageServiceImpl.getPaymentDetails - 查询到记录数: {}", list.size());
+
+        // 查询店铺信息
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+
+        // 处理每条记录的数据格式
+        for (StoreIncomeDetailsRecordVo record : list) {
+            // 空值检查
+            if (record == null) {
+                continue;
+            }
+            
+            StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
+            BeanUtils.copyProperties(record, vo);
+
+            // 转换金额格式(分转元,保留2位小数)
+            record.setMoneyStr(new BigDecimal(vo.getMoney())
+                    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+
+            // 设置日期
+            String format = df.format(record.getCreatedTime());
+            record.setDate(format);
+
+            // 设置抽成比例
+            record.setCommissionRate(storeInfo.getCommissionRate());
+
+            // 计算售价(收益 + 手续费)
+            record.setIncomeMoney(new BigDecimal(vo.getMoney())
+                    .add(new BigDecimal(vo.getCommission()))
+                    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+
+            // 设置手续费
+            record.setCommissionStr(new BigDecimal(vo.getCommission())
+                    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+
+            vo.setCommissionRate(storeInfo.getCommissionRate());
+        }
+
+        // 分页处理
+        result.put("data", ListToPage.setPage(list, page, size));
+
+        // 计算总金额(元)
+        BigDecimal totalMoney = new BigDecimal(list.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum())
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+        result.put("money", totalMoney.toString());
+
+        log.info("IncomeManageServiceImpl.getPaymentDetails - 查询完成: 总金额={}元, 记录数={}",
+                totalMoney, list.size());
+
+        return result;
+    }
+
+    /**
+     * 收入汇总统计
+     * 整合四项关键数据:可提现金额、今日收益、已入账、未入账
+     *
+     * @param storeId 门店ID
+     * @return 收入汇总统计数据
+     */
+    @Override
+    public Map<String, Object> getIncomeSummary(Integer storeId) {
+        log.info("IncomeManageServiceImpl.getIncomeSummary - 开始查询收入汇总: storeId={}", storeId);
+
+        Map<String, Object> result = new HashMap<>();
+
+        try {
+            // ==================== 1. 账户余额 & 可提现金额 ====================
+            calculateAccountBalance(storeId, result);
+
+            // ==================== 2. 今日收益 ====================
+            calculateTodayIncome(storeId, result);
+
+            // ==================== 3. 未入账统计(近3天) ====================
+            calculateNotPaidIncome(storeId, result);
+
+            // ==================== 4. 已入账统计(4-27天) ====================
+            calculatePaidIncome(storeId, result);
+
+            log.info("IncomeManageServiceImpl.getIncomeSummary - 查询完成: result={}", result);
+
+        } catch (Exception e) {
+            log.error("IncomeManageServiceImpl.getIncomeSummary - 查询失败: storeId={}, error={}", 
+                    storeId, e.getMessage(), e);
+            throw new RuntimeException("查询收入汇总失败:" + e.getMessage(), e);
+        }
+
+        return result;
+    }
+
+    /**
+     * 计算账户余额和可提现金额
+     * 
+     * 业务逻辑:
+     * 1. 账户余额 = 用户账户中的总余额(store_user.money)
+     * 2. 可提现金额 = 4-27天内未绑定提现的收入 - 待审核/已通过的提现金额
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculateAccountBalance(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 开始计算账户余额和可提现金额");
+
+        // ---------- 1.1 查询账户总余额 ----------
+        LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
+        userWrapper.eq(StoreUser::getStoreId, storeId);
+        StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
+
+        if (storeUser == null) {
+            log.warn("IncomeManageServiceImpl.calculateAccountBalance - 未找到店铺用户: storeId={}", storeId);
+            result.put("balance", "0.00");
+            result.put("cashOutMoney", "0.00");
+            return;
+        }
+
+        // 账户总余额(分转元)
+        String balance = new BigDecimal(storeUser.getMoney())
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+        result.put("balance", balance);
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 账户余额: {}元", balance);
+
+        // ---------- 1.2 查询4-27天内未绑定提现的收入 ----------
+        Date now = new Date();
+        Date startDate = DateUtils.calcDays(now, -27);  // 27天前
+        Date endDate = DateUtils.calcDays(now, -4);      // 4天前
+
+        LambdaQueryWrapper<StoreIncomeDetailsRecord> incomeWrapper = new LambdaQueryWrapper<>();
+        incomeWrapper.between(StoreIncomeDetailsRecord::getCreatedTime, startDate, endDate)
+                .isNull(StoreIncomeDetailsRecord::getCashOutId)  // 未绑定提现记录
+                .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
+
+        List<StoreIncomeDetailsRecord> incomeList = this.list(incomeWrapper);
+        int availableIncome = incomeList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 4-27天内可提现收入: {}分, 记录数: {}", 
+                availableIncome, incomeList.size());
+
+        // ---------- 1.3 查询待审核和已通过的提现金额 ----------
+        QueryWrapper<StoreCashOutRecord> cashOutWrapper = new QueryWrapper<>();
+        cashOutWrapper.eq("store_id", storeId)
+                .in("payment_status", "1", "3")  // 1-待审核, 3-已通过
+                .eq("delete_flag", "0");
+
+        List<StoreCashOutRecord> cashOutRecords = storeCashOutRecordMapper.selectList(cashOutWrapper);
+        int pendingCashOut = cashOutRecords.stream()
+                .collect(Collectors.summingInt(StoreCashOutRecord::getMoney));
+
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 待审核/已通过提现: {}分, 记录数: {}", 
+                pendingCashOut, cashOutRecords.size());
+
+        // ---------- 1.4 计算可提现金额 ----------
+        // 可提现金额 = 可提现收入 - 审核中的提现
+        int cashOutMoney = availableIncome - pendingCashOut;
+        String cashOutMoneyStr = new BigDecimal(Math.max(cashOutMoney, 0))  // 确保不为负数
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+        result.put("cashOutMoney", cashOutMoneyStr);
+
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 可提现金额: {}元", cashOutMoneyStr);
+    }
+
+    /**
+     * 计算今日收益
+     * 
+     * 业务逻辑:
+     * 统计当天00:00:00 ~ 23:59:59所有收入的总和
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculateTodayIncome(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculateTodayIncome - 开始计算今日收益");
+
+        // ---------- 2.1 获取今天的日期范围 ----------
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        String today = df.format(now);
+        String startTime = today + " 00:00:00";
+        String endTime = today + " 23:59:59";
+
+        // ---------- 2.2 查询今天的收入记录 ----------
+        LambdaQueryWrapper<StoreIncomeDetailsRecord> todayWrapper = new LambdaQueryWrapper<>();
+        todayWrapper.between(StoreIncomeDetailsRecord::getCreatedTime, startTime, endTime)
+                .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
+
+        List<StoreIncomeDetailsRecord> todayList = this.list(todayWrapper);
+
+        // ---------- 2.3 汇总今日收益 ----------
+        int todayIncome = todayList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        String todayIncomeStr = new BigDecimal(todayIncome)
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+        result.put("todayIncome", todayIncomeStr);
+
+        log.info("IncomeManageServiceImpl.calculateTodayIncome - 今日收益: {}元, 记录数: {}", 
+                todayIncomeStr, todayList.size());
+    }
+
+    /**
+     * 计算未入账统计(近3天)
+     * 
+     * 业务逻辑:
+     * 1. 时间范围:当前时间 - 3天 ~ 当前时间
+     * 2. 条件:未绑定提现记录(cash_out_id IS NULL)
+     * 3. 说明:这部分收入还未到提现账期,不能提现
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculateNotPaidIncome(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 开始计算未入账统计");
+
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+        // ---------- 3.1 计算时间范围 ----------
+        Date notPaidStartDate = DateUtils.calcDays(now, -3);  // 3天前
+        String notPaidDateRange = df.format(notPaidStartDate) + " ~ " + df.format(now);
+        result.put("notPaidDateRange", notPaidDateRange);
+
+        log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 未入账时间范围: {}", notPaidDateRange);
+
+        // ---------- 3.2 查询未入账收入(近3天,未绑定提现) ----------
+        QueryWrapper<StoreIncomeDetailsRecord> notPaidWrapper = new QueryWrapper<>();
+        notPaidWrapper.isNull("sidr.cash_out_id")  // 未绑定提现记录
+                .gt("sidr.created_time", notPaidStartDate)  // 大于3天前
+                .eq("sidr.store_id", storeId);
+
+        List<StoreIncomeDetailsRecordVo> notPaidList = storeIncomeDetailsRecordMapper.selectRecordList(notPaidWrapper);
+
+        // ---------- 3.3 汇总未入账金额和数量 ----------
+        int notPaidMoney = notPaidList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        String notPaidMoneyStr = new BigDecimal(notPaidMoney)
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+
+        result.put("notPaidMoney", notPaidMoneyStr);
+        result.put("notPaidCount", notPaidList.size());
+
+        log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 未入账: {}元, 记录数: {}", 
+                notPaidMoneyStr, notPaidList.size());
+    }
+
+    /**
+     * 计算已入账统计(4-27天)
+     * 
+     * 业务逻辑:
+     * 1. 时间范围:当前时间 - 27天 ~ 当前时间 - 4天
+     * 2. 说明:这部分收入已到提现账期,可以申请提现
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculatePaidIncome(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculatePaidIncome - 开始计算已入账统计");
+
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+        // ---------- 4.1 计算时间范围 ----------
+        Date paidStartDate = DateUtils.calcDays(now, -27);  // 27天前
+        Date paidEndDate = DateUtils.calcDays(now, -4);      // 4天前
+        String paidDateRange = df.format(paidStartDate) + " ~ " + df.format(paidEndDate);
+        result.put("paidDateRange", paidDateRange);
+
+        log.info("IncomeManageServiceImpl.calculatePaidIncome - 已入账时间范围: {}", paidDateRange);
+
+        // ---------- 4.2 查询已入账收入(4-27天前) ----------
+        QueryWrapper<StoreIncomeDetailsRecord> paidWrapper = new QueryWrapper<>();
+        paidWrapper.between("sidr.created_time", paidStartDate, paidEndDate)
+                .eq("sidr.store_id", storeId);
+
+        List<StoreIncomeDetailsRecordVo> paidList = storeIncomeDetailsRecordMapper.selectRecordList(paidWrapper);
+
+        // ---------- 4.3 汇总已入账金额和数量 ----------
+        int paidMoney = paidList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        String paidMoneyStr = new BigDecimal(paidMoney)
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+
+        result.put("paidMoney", paidMoneyStr);
+        result.put("paidCount", paidList.size());
+
+        log.info("IncomeManageServiceImpl.calculatePaidIncome - 已入账: {}元, 记录数: {}", 
+                paidMoneyStr, paidList.size());
+    }
+}
+

+ 197 - 5
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LicenseServiceImpl.java

@@ -3,32 +3,224 @@ package shop.alien.storeplatform.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
-import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.*;
+import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryDto;
+import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
+import shop.alien.entity.storePlatform.StoreLicenseHistory;
 import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.StoreInfoMapper;
+import shop.alien.mapper.storePlantform.StoreLicenseHistoryMapper;
 import shop.alien.storeplatform.service.LicenseService;
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.List;
+import java.util.Optional;
 
-
+/**
+ * 证照管理服务实现类
+ * <p>
+ * 提供商户证照图片和证照历史记录的管理功能,包括:
+ * 1. 营业执照、合同、食品经营许可证等图片的查询
+ * 2. 证照历史记录的增删改查操作
+ * </p>
+ *
+ * @author system
+ * @since 2025-11-24
+ */
 @Slf4j
 @Service
 @RequiredArgsConstructor
 public class LicenseServiceImpl implements LicenseService {
 
+    /** 商户图片数据访问对象 */
     private final StoreImgMapper storeImgMapper;
 
+    /** 商户证照历史记录数据访问对象 */
+    private final StoreLicenseHistoryMapper licenseHistoryMapper;
+
+    private final StoreInfoMapper storeInfoMapper;
+
+
+    @Override
+    public int uploadfoodLicence(StoreImg storeImg) {
+        storeImgMapper.delete(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType,24).eq(StoreImg::getStoreId,storeImg.getStoreId()));
+        storeImg.setImgType(24);
+        storeImg.setImgDescription("经营许可证审核通过前图片");
+        storeImgMapper.insert(storeImg);
+
+        // 经营许可证历史表插入
+        StoreLicenseHistory licenseHistory = new StoreLicenseHistory();
+        licenseHistory.setStoreId(storeImg.getStoreId());
+        licenseHistory.setLicenseStatus(2);
+        licenseHistory.setLicenseExecuteStatus(2);
+        licenseHistory.setImgUrl(storeImg.getImgUrl());
+        licenseHistory.setDeleteFlag(0);
+        licenseHistoryMapper.insert(licenseHistory);
+
+        //更新店铺
+        StoreInfo storeInfo = new StoreInfo();
+        storeInfo.setFoodLicenceStatus(2);
+        storeInfo.setId(storeImg.getStoreId());
+        storeInfo.setUpdateFoodLicenceTime(new Date());
+        return storeInfoMapper.updateById(storeInfo);
+    }
+
+    /**
+     * 获取营业执照图片列表
+     * <p>
+     * 查询指定商户的营业执照图片信息(图片类型为14)
+     * </p>
+     *
+     * @param id 商户ID
+     * @return 营业执照图片列表
+     */
+    @Override
     public List<StoreImg> getbusinessLicenseList (Integer id) {
-        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType, 14).eq(StoreImg::getStoreId, id));
+        // 查询图片类型为14(营业执照)的图片列表
+        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getImgType, 14)
+                .eq(StoreImg::getStoreId, id));
     }
 
+    /**
+     * 获取合同图片列表
+     * <p>
+     * 查询指定商户的合同图片信息(图片类型为15)
+     * </p>
+     *
+     * @param id 商户ID
+     * @return 合同图片列表
+     */
+    @Override
     public List<StoreImg> getContractList (Integer id) {
-        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType, 15).eq(StoreImg::getStoreId, id));
+        // 查询图片类型为15(合同)的图片列表
+        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getImgType, 15)
+                .eq(StoreImg::getStoreId, id));
     }
 
+    /**
+     * 获取食品经营许可证图片列表
+     * <p>
+     * 查询指定商户的食品经营许可证图片信息(图片类型为25)
+     * </p>
+     *
+     * @param id 商户ID
+     * @return 食品经营许可证图片列表
+     */
+    @Override
     public List<StoreImg> getFoodLicenceList (Integer id) {
-        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType, 25).eq(StoreImg::getStoreId, id));
+        // 查询图片类型为25(食品经营许可证)的图片列表
+        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getImgType, 25)
+                .eq(StoreImg::getStoreId, id));
+    }
+
+    /**
+     * 根据商户ID查询证照历史记录列表(带格式化日期)
+     * <p>
+     * 查询指定商户的证照历史记录,将创建时间格式化为 yyyy-MM-dd 格式,
+     * 并转换为 VO 对象返回
+     * </p>
+     *
+     * @param storeId 商户ID
+     * @return 证照历史记录VO列表
+     */
+    @Override
+    public List<StoreLicenseHistoryDto> queryLicenceByStatusList (Integer storeId) {
+
+        List<StoreLicenseHistory> licenseList = licenseHistoryMapper.queryLicenceByStatusList(storeId, 2, 1);
+        
+        // 创建日期格式化对象,格式为 yyyy-MM-dd
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        
+        // 创建返回的 VO 列表
+        List<StoreLicenseHistoryDto> voList = new java.util.ArrayList<>();
+        
+        for (StoreLicenseHistory license : licenseList) {
+            // 创建 VO 对象
+            StoreLicenseHistoryDto vo = new StoreLicenseHistoryDto();
+
+            // 将实体对象属性复制到 VO 对象
+            BeanUtils.copyProperties(license, vo);
+            
+            // 将创建时间格式化为 yyyy-MM-dd 格式,去除时分秒
+            if (license.getCreatedTime() != null) {
+                String dateStr = sdf.format(license.getCreatedTime());
+                vo.setCreatedDateFormat(dateStr);
+            }
+            
+            // 设置审核状态名称
+            if (license.getLicenseExecuteStatus() != null) {
+                switch (license.getLicenseExecuteStatus()) {
+                    case 1:
+                        vo.setLicenseExecuteName("审核通过");
+                        break;
+                    case 2:
+                        vo.setLicenseExecuteName("审核中");
+                        break;
+                    case 3:
+                        vo.setLicenseExecuteName("审核失败");
+                        break;
+                    default:
+                        vo.setLicenseExecuteName("未知");
+                        break;
+                }
+            }
+            
+            voList.add(vo);
+        }
+        
+        return voList;
     }
 
+
+    @Override
+    public int uploadRenewalContract(List<StoreImg> storeImgList) {
+        //取出集合中门店id
+        Optional<Integer> storeId = storeImgList.stream().map(StoreImg::getStoreId).findFirst();
+        int value = storeId.orElse(0);
+        int num = 0;
+        //先清除数据
+        storeImgMapper.delete(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType,22).eq(StoreImg::getStoreId,value));
+        for (StoreImg renewContract : storeImgList) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(renewContract.getStoreId());
+            storeImg.setImgType(22);
+            storeImg.setImgSort(renewContract.getImgSort());
+            storeImg.setImgDescription("续签合同图片");
+            storeImg.setImgUrl(renewContract.getImgUrl());
+            storeImg.setCreatedUserId(renewContract.getCreatedUserId());
+            num = storeImgMapper.insert(storeImg);
+
+            // 经营许可证历史表删除
+            licenseHistoryMapper.delete(new LambdaQueryWrapper<StoreLicenseHistory>()
+                    .eq(StoreLicenseHistory::getLicenseStatus,1)
+                    .eq(StoreLicenseHistory::getStoreId,storeImg.getStoreId()));
+            // 经营许可证历史表插入
+            StoreLicenseHistory licenseHistory = new StoreLicenseHistory();
+            licenseHistory.setStoreId(storeImg.getStoreId());
+            licenseHistory.setLicenseStatus(1);
+            licenseHistory.setLicenseExecuteStatus(2);
+            licenseHistory.setImgUrl(storeImg.getImgUrl());
+            licenseHistory.setDeleteFlag(0);
+            licenseHistoryMapper.insert(licenseHistory);
+        }
+        if (num > 0) {
+            StoreInfo storeInfo = new StoreInfo();
+            storeInfo.setRenewContractStatus(2);
+            storeInfo.setUpdateRenewContractTime(new Date());
+            storeInfo.setId(value);
+            storeInfoMapper.updateById(storeInfo);
+        }
+        return num;
+    }
+
+
+
 }
 

+ 219 - 2
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/MerchantUserServiceImpl.java

@@ -1,15 +1,18 @@
 package shop.alien.storeplatform.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
-import shop.alien.entity.store.StoreUser;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.*;
 import shop.alien.entity.store.vo.StoreUserVo;
-import shop.alien.mapper.StoreUserMapper;
+import shop.alien.mapper.*;
 import shop.alien.storeplatform.service.MerchantUserService;
+import shop.alien.storeplatform.util.LoginUserUtil;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -31,6 +34,15 @@ import java.util.Map;
 public class MerchantUserServiceImpl implements MerchantUserService {
 
     private final StoreUserMapper storeUserMapper;
+    private final StoreInfoMapper storeInfoMapper;
+    private final StoreImgMapper storeImgMapper;
+    private final TagStoreRelationMapper tagStoreRelationMapper;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final LifeUserOrderMapper lifeUserOrderMapper;
+    private final OrderCouponMiddleMapper orderCouponMiddleMapper;
+    private final LifeCouponMapper lifeCouponMapper;
+    private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
+    private final LifeDiscountCouponUserMapper lifeDiscountCouponUserMapper;
 
     @Override
     public StoreUserVo getMerchantByPhone(String phone) {
@@ -193,6 +205,211 @@ public class MerchantUserServiceImpl implements MerchantUserService {
         log.info("MerchantUserServiceImpl.checkPayPassword - 检查完成: storeUserId={}, result=true", storeUserId);
         return returnMap;
     }
+
+    /**
+     * 设置/修改支付密码
+     * 
+     * @param id          用户ID
+     * @param payPassword 支付密码
+     * @return 是否设置成功
+     */
+    @Override
+    public boolean setPayPassword(Integer id, String payPassword) {
+        log.info("MerchantUserServiceImpl.setPayPassword - 开始设置支付密码: id={}", id);
+
+        // 1. 验证参数
+        if (id == null) {
+            log.warn("MerchantUserServiceImpl.setPayPassword - 用户ID为空");
+            throw new RuntimeException("用户ID不能为空");
+        }
+
+        if (StringUtils.isEmpty(payPassword)) {
+            log.warn("MerchantUserServiceImpl.setPayPassword - 支付密码为空");
+            throw new RuntimeException("支付密码不能为空");
+        }
+
+        // 2. 构建更新对象
+        StoreUser storeUser = new StoreUser();
+        storeUser.setId(id);
+        storeUser.setPayPassword(payPassword);
+
+        // 3. 执行更新
+        int updateResult = storeUserMapper.updateById(storeUser);
+        boolean success = updateResult > 0;
+
+        log.info("MerchantUserServiceImpl.setPayPassword - 设置完成: id={}, success={}", id, success);
+        return success;
+    }
+
+    /**
+     * 添加/修改支付宝账号
+     *
+     * @param userId        用户ID
+     * @param alipayAccount 支付宝账号
+     * @return 影响的行数
+     */
+    @Override
+    public Integer addAlipayAccount(Integer userId, String alipayAccount) {
+        log.info("MerchantUserServiceImpl.addAlipayAccount - 开始添加支付宝账号: userId={}, alipayAccount={}", 
+                userId, alipayAccount);
+
+        // 1. 验证参数
+        if (userId == null) {
+            log.warn("MerchantUserServiceImpl.addAlipayAccount - 用户ID为空");
+            throw new RuntimeException("用户ID不能为空");
+        }
+
+        if (StringUtils.isEmpty(alipayAccount)) {
+            log.warn("MerchantUserServiceImpl.addAlipayAccount - 支付宝账号为空");
+            throw new RuntimeException("支付宝账号不能为空");
+        }
+
+        // 2. 构建更新条件
+        LambdaQueryWrapper<StoreUser> updateWrapper = new LambdaQueryWrapper<>();
+        updateWrapper.eq(StoreUser::getId, userId);
+
+        // 3. 构建更新对象
+        StoreUser storeUser = new StoreUser();
+        storeUser.setAlipayAccount(alipayAccount);
+
+        // 4. 执行更新
+        int updateResult = storeUserMapper.update(storeUser, updateWrapper);
+
+        log.info("MerchantUserServiceImpl.addAlipayAccount - 添加完成: userId={}, updateResult={}", 
+                userId, updateResult);
+
+        return updateResult;
+    }
+
+    /**
+     * 重置商户到刚注册状态
+     * 一键退回:删除店铺申请、解绑店铺、清理相关数据
+     *
+     * @return 是否重置成功
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean resetToInitialStatus() {
+        try {
+            // 1. 获取当前登录用户
+            Integer userId = LoginUserUtil.getCurrentUserId();
+            StoreUser storeUser = storeUserMapper.selectById(userId);
+            
+            if (storeUser == null) {
+                log.warn("MerchantUserServiceImpl.resetToInitialStatus - 用户不存在: userId={}", userId);
+                return false;
+            }
+            
+            Integer storeId = storeUser.getStoreId();
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 开始重置: userId={}, storeId={}", 
+                    userId, storeId);
+            
+            // 2. 如果没有关联店铺,无需重置
+            if (storeId == null) {
+                log.info("MerchantUserServiceImpl.resetToInitialStatus - 用户未关联店铺,无需重置");
+                return true;
+            }
+            
+            // 3. 删除店铺图片(营业执照、合同、经营许可证)
+            LambdaQueryWrapper<StoreImg> imgWrapper = new LambdaQueryWrapper<>();
+            imgWrapper.eq(StoreImg::getStoreId, storeId)
+                    .in(StoreImg::getImgType, 14, 15, 25);  // 14-营业执照, 15-合同, 25-经营许可证
+            int deletedImgCount = storeImgMapper.delete(imgWrapper);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除店铺图片: count={}", deletedImgCount);
+            
+            // 4. 删除店铺标签关系
+            LambdaQueryWrapper<TagStoreRelation> tagWrapper = new LambdaQueryWrapper<>();
+            tagWrapper.eq(TagStoreRelation::getStoreId, storeId);
+            int deletedTagCount = tagStoreRelationMapper.delete(tagWrapper);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除店铺标签: count={}", deletedTagCount);
+            
+            // 5. 删除店铺信息
+            int deletedStoreCount = storeInfoMapper.deleteById(storeId);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除店铺信息: success={}", deletedStoreCount > 0);
+            
+            // 6. 清理用户关联的通知消息(入住申请相关)
+            if (StringUtils.isNotEmpty(storeUser.getPhone())) {
+                String receiverId = "store_" + storeUser.getPhone();
+                LambdaQueryWrapper<LifeNotice> noticeWrapper = new LambdaQueryWrapper<>();
+                noticeWrapper.eq(LifeNotice::getReceiverId, receiverId)
+                        .eq(LifeNotice::getTitle, "入住店铺申请");
+                int deletedNoticeCount = lifeNoticeMapper.delete(noticeWrapper);
+                log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除入住申请通知: count={}", deletedNoticeCount);
+            }
+            
+            // 7. 查询该店铺的所有订单ID(用于后续删除验券记录)
+            LambdaQueryWrapper<LifeUserOrder> orderQueryWrapper = new LambdaQueryWrapper<>();
+            orderQueryWrapper.eq(LifeUserOrder::getStoreId, storeId)
+                    .select(LifeUserOrder::getId);
+            java.util.List<LifeUserOrder> orderList = lifeUserOrderMapper.selectList(orderQueryWrapper);
+            java.util.List<String> orderIds = orderList.stream()
+                    .map(order -> order.getId().toString())
+                    .collect(java.util.stream.Collectors.toList());
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 查询到订单数量: count={}", orderIds.size());
+            
+            // 8. 删除订单-优惠券中间表数据(验券记录)
+            if (!orderIds.isEmpty()) {
+                LambdaQueryWrapper<OrderCouponMiddle> orderCouponWrapper = new LambdaQueryWrapper<>();
+                orderCouponWrapper.in(OrderCouponMiddle::getOrderId, orderIds);
+                int deletedOrderCouponCount = orderCouponMiddleMapper.delete(orderCouponWrapper);
+                log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除验券记录: count={}", deletedOrderCouponCount);
+            }
+            
+            // 9. 删除该店铺的所有订单记录
+            LambdaQueryWrapper<LifeUserOrder> orderWrapper = new LambdaQueryWrapper<>();
+            orderWrapper.eq(LifeUserOrder::getStoreId, storeId);
+            int deletedOrderCount = lifeUserOrderMapper.delete(orderWrapper);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除订单记录: count={}", deletedOrderCount);
+            
+            // 10. 删除该店铺发布的团购券
+            LambdaQueryWrapper<LifeCoupon> couponWrapper = new LambdaQueryWrapper<>();
+            couponWrapper.eq(LifeCoupon::getStoreId, storeId);
+            int deletedCouponCount = lifeCouponMapper.delete(couponWrapper);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除团购券: count={}", deletedCouponCount);
+            
+            // 10. 删除该店铺发布的代金券
+            LambdaQueryWrapper<LifeDiscountCoupon> discountCouponWrapper = new LambdaQueryWrapper<>();
+            discountCouponWrapper.eq(LifeDiscountCoupon::getStoreId, storeId);
+            int deletedDiscountCouponCount = lifeDiscountCouponMapper.delete(discountCouponWrapper);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除代金券: count={}", deletedDiscountCouponCount);
+            
+            // 11. 删除用户领取的优惠券(用户是商户本人)
+            LambdaQueryWrapper<LifeDiscountCouponUser> couponUserWrapper = new LambdaQueryWrapper<>();
+            couponUserWrapper.eq(LifeDiscountCouponUser::getUserId, userId);
+            int deletedCouponUserCount = lifeDiscountCouponUserMapper.delete(couponUserWrapper);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除用户优惠券: count={}", deletedCouponUserCount);
+            
+            // 12. 清理所有通知消息(包括订单、优惠券相关)
+            if (StringUtils.isNotEmpty(storeUser.getPhone())) {
+                String receiverId = "store_" + storeUser.getPhone();
+                LambdaQueryWrapper<LifeNotice> allNoticeWrapper = new LambdaQueryWrapper<>();
+                allNoticeWrapper.eq(LifeNotice::getReceiverId, receiverId);
+                int deletedAllNoticeCount = lifeNoticeMapper.delete(allNoticeWrapper);
+                log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除所有通知消息: count={}", deletedAllNoticeCount);
+            }
+            
+            // 13. 重置用户的店铺关联和个人信息(使用 LambdaUpdateWrapper 显式设置为 null)
+            LambdaUpdateWrapper<StoreUser> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(StoreUser::getId, userId)
+                    .set(StoreUser::getStoreId, null)       // 解绑店铺
+                    .set(StoreUser::getName, null)          // 清空姓名(入住时填写的联系人)
+                    .set(StoreUser::getIdCard, null)        // 清空身份证(入住时填写的)
+                    .set(StoreUser::getPayPassword, null)   // 清空支付密码
+                    .set(StoreUser::getMoney, 0)    // 重置用户余额为0
+                    .set(StoreUser::getAlipayAccount, null); // 清空支付宝账号(入住时填写的)
+            
+            int updateResult = storeUserMapper.update(null, updateWrapper);
+            
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 重置用户信息: success={}", updateResult > 0);
+            log.info("MerchantUserServiceImpl.resetToInitialStatus - 重置完成: userId={}, storeId={}", userId, storeId);
+            
+            return updateResult > 0;
+            
+        } catch (Exception e) {
+            log.error("MerchantUserServiceImpl.resetToInitialStatus - 重置失败: {}", e.getMessage(), e);
+            throw new RuntimeException("重置失败:" + e.getMessage(), e);
+        }
+    }
 }
 
 

+ 0 - 241
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreLicenseHistoryServiceImpl.java

@@ -1,241 +0,0 @@
-package shop.alien.storeplatform.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.BeanUtils;
-import org.springframework.stereotype.Service;
-import shop.alien.entity.result.BusinessException;
-import shop.alien.entity.store.StoreLicenseHistory;
-import shop.alien.entity.store.StoreInfo;
-import shop.alien.entity.store.dto.StoreLicenseHistoryDTO;
-import shop.alien.entity.store.vo.StoreLicenseHistoryVO;
-import shop.alien.mapper.StoreLicenseHistoryMapper;
-import shop.alien.mapper.StoreInfoMapper;
-import shop.alien.storeplatform.service.StoreLicenseHistoryService;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * 商户证照历史记录 Service 实现类
- *
- * @author system
- * @since 2025-11-24
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class StoreLicenseHistoryServiceImpl implements StoreLicenseHistoryService {
-
-    private final StoreLicenseHistoryMapper storeLicenseHistoryMapper;
-    private final StoreInfoMapper storeInfoMapper;
-
-    @Override
-    public StoreLicenseHistory createLicenseHistory(StoreLicenseHistoryDTO dto) {
-        log.info("StoreLicenseHistoryServiceImpl.createLicenseHistory: dto={}", dto);
-        
-        // 验证商户是否存在
-        StoreInfo storeInfo = storeInfoMapper.selectById(dto.getStoreId());
-        if (storeInfo == null) {
-            throw new BusinessException("商户不存在");
-        }
-
-        // 验证证照类型
-        if (dto.getLicenseStatus() == null || (dto.getLicenseStatus() != 1 && dto.getLicenseStatus() != 2)) {
-            throw new BusinessException("证照类型无效");
-        }
-
-        // 验证审核状态
-        if (dto.getLicenseExecuteStatus() == null || 
-            (dto.getLicenseExecuteStatus() < 1 || dto.getLicenseExecuteStatus() > 3)) {
-            throw new BusinessException("审核状态无效");
-        }
-
-        StoreLicenseHistory entity = new StoreLicenseHistory();
-        BeanUtils.copyProperties(dto, entity);
-        
-        int result = storeLicenseHistoryMapper.insert(entity);
-        if (result > 0) {
-            return entity;
-        }
-        throw new BusinessException("创建证照历史记录失败");
-    }
-
-    @Override
-    public boolean updateLicenseHistory(StoreLicenseHistoryDTO dto) {
-        log.info("StoreLicenseHistoryServiceImpl.updateLicenseHistory: dto={}", dto);
-        
-        if (dto.getId() == null) {
-            throw new BusinessException("记录ID不能为空");
-        }
-
-        StoreLicenseHistory existingRecord = storeLicenseHistoryMapper.selectById(dto.getId());
-        if (existingRecord == null) {
-            throw new BusinessException("证照历史记录不存在");
-        }
-
-        StoreLicenseHistory entity = new StoreLicenseHistory();
-        BeanUtils.copyProperties(dto, entity);
-        
-        int result = storeLicenseHistoryMapper.updateById(entity);
-        return result > 0;
-    }
-
-    @Override
-    public boolean deleteLicenseHistory(Integer id) {
-        log.info("StoreLicenseHistoryServiceImpl.deleteLicenseHistory: id={}", id);
-        
-        if (id == null) {
-            throw new BusinessException("记录ID不能为空");
-        }
-
-        StoreLicenseHistory existingRecord = storeLicenseHistoryMapper.selectById(id);
-        if (existingRecord == null) {
-            throw new BusinessException("证照历史记录不存在");
-        }
-
-        // 逻辑删除
-        int result = storeLicenseHistoryMapper.deleteById(id);
-        return result > 0;
-    }
-
-    @Override
-    public StoreLicenseHistoryVO getLicenseHistoryById(Integer id) {
-        log.info("StoreLicenseHistoryServiceImpl.getLicenseHistoryById: id={}", id);
-        
-        if (id == null) {
-            throw new BusinessException("记录ID不能为空");
-        }
-
-        StoreLicenseHistory entity = storeLicenseHistoryMapper.selectById(id);
-        if (entity == null) {
-            return null;
-        }
-
-        return convertToVO(entity);
-    }
-
-    @Override
-    public List<StoreLicenseHistoryVO> getLicenseHistoryListByStoreId(Integer storeId) {
-        log.info("StoreLicenseHistoryServiceImpl.getLicenseHistoryListByStoreId: storeId={}", storeId);
-        
-        if (storeId == null) {
-            throw new BusinessException("商户ID不能为空");
-        }
-
-        LambdaQueryWrapper<StoreLicenseHistory> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(StoreLicenseHistory::getStoreId, storeId)
-               .orderByDesc(StoreLicenseHistory::getCreatedTime);
-        
-        List<StoreLicenseHistory> list = storeLicenseHistoryMapper.selectList(wrapper);
-        return list.stream().map(this::convertToVO).collect(Collectors.toList());
-    }
-
-    @Override
-    public List<StoreLicenseHistoryVO> getLicenseHistoryListByStoreIdAndType(Integer storeId, Integer licenseStatus) {
-        log.info("StoreLicenseHistoryServiceImpl.getLicenseHistoryListByStoreIdAndType: storeId={}, licenseStatus={}", 
-                 storeId, licenseStatus);
-        
-        if (storeId == null) {
-            throw new BusinessException("商户ID不能为空");
-        }
-        if (licenseStatus == null) {
-            throw new BusinessException("证照类型不能为空");
-        }
-
-        LambdaQueryWrapper<StoreLicenseHistory> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(StoreLicenseHistory::getStoreId, storeId)
-               .eq(StoreLicenseHistory::getLicenseStatus, licenseStatus)
-               .orderByDesc(StoreLicenseHistory::getCreatedTime);
-        
-        List<StoreLicenseHistory> list = storeLicenseHistoryMapper.selectList(wrapper);
-        return list.stream().map(this::convertToVO).collect(Collectors.toList());
-    }
-
-    @Override
-    public Page<StoreLicenseHistoryVO> getLicenseHistoryPage(Page<StoreLicenseHistory> page, Integer storeId,
-                                                              Integer licenseStatus, Integer licenseExecuteStatus) {
-        log.info("StoreLicenseHistoryServiceImpl.getLicenseHistoryPage: page={}, storeId={}, licenseStatus={}, licenseExecuteStatus={}", 
-                 page, storeId, licenseStatus, licenseExecuteStatus);
-        
-        LambdaQueryWrapper<StoreLicenseHistory> wrapper = new LambdaQueryWrapper<>();
-        
-        if (storeId != null) {
-            wrapper.eq(StoreLicenseHistory::getStoreId, storeId);
-        }
-        if (licenseStatus != null) {
-            wrapper.eq(StoreLicenseHistory::getLicenseStatus, licenseStatus);
-        }
-        if (licenseExecuteStatus != null) {
-            wrapper.eq(StoreLicenseHistory::getLicenseExecuteStatus, licenseExecuteStatus);
-        }
-        
-        wrapper.orderByDesc(StoreLicenseHistory::getCreatedTime);
-        
-        IPage<StoreLicenseHistory> entityPage = storeLicenseHistoryMapper.selectPage(page, wrapper);
-        
-        // 转换为VO
-        Page<StoreLicenseHistoryVO> voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
-        List<StoreLicenseHistoryVO> voList = entityPage.getRecords().stream()
-                .map(this::convertToVO)
-                .collect(Collectors.toList());
-        voPage.setRecords(voList);
-        
-        return voPage;
-    }
-
-    /**
-     * 实体转VO
-     */
-    private StoreLicenseHistoryVO convertToVO(StoreLicenseHistory entity) {
-        StoreLicenseHistoryVO vo = new StoreLicenseHistoryVO();
-        BeanUtils.copyProperties(entity, vo);
-        
-        // 获取商户名称
-        if (entity.getStoreId() != null) {
-            StoreInfo storeInfo = storeInfoMapper.selectById(entity.getStoreId());
-            if (storeInfo != null) {
-                vo.setStoreName(storeInfo.getStoreName());
-            }
-        }
-        
-        // 设置证照类型名称
-        if (entity.getLicenseStatus() != null) {
-            switch (entity.getLicenseStatus()) {
-                case 1:
-                    vo.setLicenseStatusName("合同管理");
-                    break;
-                case 2:
-                    vo.setLicenseStatusName("食品经营许可证");
-                    break;
-                default:
-                    vo.setLicenseStatusName("未知");
-                    break;
-            }
-        }
-        
-        // 设置审核状态名称
-        if (entity.getLicenseExecuteStatus() != null) {
-            switch (entity.getLicenseExecuteStatus()) {
-                case 1:
-                    vo.setLicenseExecuteStatusName("审核中");
-                    break;
-                case 2:
-                    vo.setLicenseExecuteStatusName("审核拒绝");
-                    break;
-                case 3:
-                    vo.setLicenseExecuteStatusName("审核通过");
-                    break;
-                default:
-                    vo.setLicenseExecuteStatusName("未知");
-                    break;
-            }
-        }
-        
-        return vo;
-    }
-}
-

+ 3 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/util/LoginUserUtil.java

@@ -247,3 +247,6 @@ public class LoginUserUtil {
 
 
 
+
+
+

+ 5 - 0
alien-store-platform/接口文档/19-查询账户余额接口.md

@@ -717,3 +717,8 @@ ADD INDEX idx_store_status (store_id, payment_status, delete_flag);
 **维护人员**: ssk
 
 
+
+
+
+
+

+ 5 - 0
alien-store-platform/接口文档/20-修改商户用户信息接口.md

@@ -792,3 +792,8 @@ if (StringUtils.isNotEmpty(storeUser.getIdCard())) {
 **维护人员**: ssk
 
 
+
+
+
+
+

+ 1 - 0
alien-store-platform/接口文档/21-检查支付密码接口.md

@@ -891,3 +891,4 @@ if (response.data.data.data === true) { ... }
 
 
 
+

+ 1 - 0
alien-store-platform/接口文档/22-批量标记通知已读接口-DTO版.md

@@ -764,3 +764,4 @@ ADD INDEX idx_receiver_read_type (receiver_id, is_read, notice_type);
 
 
 
+

+ 930 - 0
alien-store-platform/接口文档/29-重置到刚注册状态接口.md

@@ -0,0 +1,930 @@
+# Web端商户重置到刚注册状态接口文档
+
+## 模块概述
+
+本接口提供一键重置功能,可以将商户状态退回到刚注册时的初始状态,清理所有入住申请、订单、优惠券、验券、消息等相关数据。适用于测试环境或商户需要重新申请入住的场景。
+
+---
+
+## 接口信息
+
+### 重置商户到刚注册状态
+
+#### 接口详情
+
+- **接口名称**: 重置商户到刚注册状态
+- **接口路径**: `POST /merchantUser/resetToInitialStatus`
+- **请求方式**: POST
+- **接口描述**: 一键清理入住申请、订单、优惠券、验券、消息等数据,解绑店铺,将商户退回到刚注册的初始状态
+- **登录验证**: ✅ 需要(通过 JWT token 自动获取用户ID)
+- **事务保护**: ✅ 使用 @Transactional 保证原子性
+
+---
+
+## 业务逻辑说明
+
+### 重置操作内容
+
+在商户申请入住及业务运营时,系统会进行以下数据变更:
+
+#### **入住申请相关**:
+1. ✅ **store_info 表** - 新增店铺记录
+2. ✅ **store_user 表** - 更新 storeId、name、idCard
+3. ✅ **store_img 表** - 新增图片记录(营业执照、合同、经营许可证)
+4. ✅ **tag_store_relation 表** - 新增店铺标签关系
+5. ✅ **life_notice 表** - 新增入住申请通知
+
+#### **业务运营相关**:
+6. ✅ **life_user_order 表** - 订单记录(用户购买团购券、代金券的订单)
+7. ✅ **order_coupon_middle 表** - 验券记录(订单-优惠券中间表)
+8. ✅ **life_coupon 表** - 团购券(店铺发布的团购券)
+9. ✅ **life_discount_coupon 表** - 代金券(店铺发布的代金券)
+10. ✅ **life_discount_coupon_user 表** - 用户领取的优惠券
+11. ✅ **life_notice 表** - 所有通知消息(订单、优惠券、系统通知等)
+
+#### **其他**:
+12. ⚠️ **Redis** - 地理位置信息(需手动清理)
+
+**重置接口会清理以上所有数据,恢复到刚注册状态。**
+
+---
+
+### 重置流程
+
+```
+┌─────────────────────────────────────────────┐
+│  1. 获取当前登录用户                          │
+│  userId = LoginUserUtil.getCurrentUserId()  │
+│  storeUser = selectById(userId)              │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  2. 检查是否有关联店铺                        │
+│  IF storeId == null                          │
+│    返回成功(无需重置)                       │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  3. 删除店铺图片                              │
+│  DELETE FROM store_img                       │
+│  WHERE store_id = ? AND img_type IN (14,15,25)│
+│  - 14: 营业执照                              │
+│  - 15: 合同图片                              │
+│  - 25: 经营许可证                            │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  4. 删除店铺标签关系                          │
+│  DELETE FROM tag_store_relation              │
+│  WHERE store_id = ?                          │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  5. 删除店铺信息                              │
+│  DELETE FROM store_info                      │
+│  WHERE id = ?                                │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  6. 清理入住申请通知                          │
+│  DELETE FROM life_notice                     │
+│  WHERE receiver_id = 'store_手机号'          │
+│    AND title = '入住店铺申请'                │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  7. 查询该店铺的所有订单ID                    │
+│  SELECT id FROM life_user_order              │
+│  WHERE store_id = ?                          │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  8. 删除订单-优惠券中间表(验券记录)         │
+│  DELETE FROM order_coupon_middle             │
+│  WHERE order_id IN (订单ID列表)              │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  9. 删除该店铺的所有订单记录                  │
+│  DELETE FROM life_user_order                 │
+│  WHERE store_id = ?                          │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  10. 删除该店铺发布的团购券                   │
+│  DELETE FROM life_coupon                     │
+│  WHERE store_id = ?                          │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  11. 删除该店铺发布的代金券                   │
+│  DELETE FROM life_discount_coupon            │
+│  WHERE store_id = ?                          │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  12. 删除用户领取的优惠券                     │
+│  DELETE FROM life_discount_coupon_user       │
+│  WHERE user_id = ?                           │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  13. 清理所有通知消息                         │
+│  DELETE FROM life_notice                     │
+│  WHERE receiver_id = 'store_手机号'          │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  14. 重置用户信息                             │
+│  UPDATE store_user SET                       │
+│    store_id = NULL,                          │
+│    name = NULL,                              │
+│    id_card = NULL                            │
+│  WHERE id = ?                                │
+└─────────────────┬───────────────────────────┘
+                  ↓
+┌─────────────────────────────────────────────┐
+│  15. 返回结果                                 │
+│  返回成功:"已退回到刚注册状态"              │
+└─────────────────────────────────────────────┘
+```
+
+---
+
+## 请求参数
+
+**无参数**
+
+从 JWT token 自动获取当前登录用户的 userId。
+
+---
+
+## 请求示例
+
+```http
+POST /merchantUser/resetToInitialStatus
+Headers:
+  Authorization: Bearer YOUR_JWT_TOKEN
+  Content-Type: application/json
+```
+
+```bash
+curl -X POST "http://localhost:8080/merchantUser/resetToInitialStatus" \
+  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
+  -H "Content-Type: application/json"
+```
+
+---
+
+## 响应参数
+
+### 成功响应
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": true,
+    "msg": "重置成功,已退回到刚注册状态"
+}
+```
+
+### 响应字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| code | Integer | 状态码(200-成功) |
+| success | Boolean | 是否成功 |
+| data | Boolean | 重置结果(true-成功) |
+| msg | String | 提示信息 |
+
+---
+
+### 失败响应
+
+#### 1. 未登录或 token 无效
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "请先登录"
+}
+```
+
+#### 2. 用户不存在
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "重置失败:用户不存在"
+}
+```
+
+#### 3. 数据库操作失败
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "重置失败:{异常信息}"
+}
+```
+
+---
+
+## 数据库操作详情
+
+### 1. 删除店铺图片
+
+```sql
+DELETE FROM store_img
+WHERE store_id = ?
+  AND img_type IN (14, 15, 25)
+```
+
+**img_type 说明**:
+- `14`: 营业执照图片
+- `15`: 合同图片
+- `25`: 经营许可证图片
+
+---
+
+### 2. 删除店铺标签关系
+
+```sql
+DELETE FROM tag_store_relation
+WHERE store_id = ?
+```
+
+---
+
+### 3. 删除店铺信息
+
+```sql
+DELETE FROM store_info
+WHERE id = ?
+```
+
+**影响范围**:
+- 店铺名称、地址、联系方式
+- 经营板块、经营种类
+- 审批状态、店铺状态
+- 抽成比例、经纬度等
+
+---
+
+### 4. 清理入住申请通知
+
+```sql
+DELETE FROM life_notice
+WHERE receiver_id = ?
+  AND title = '入住店铺申请'
+```
+
+**receiver_id 格式**: `store_{手机号}`
+
+---
+
+### 5. 查询店铺的所有订单ID
+
+```sql
+SELECT id
+FROM life_user_order
+WHERE store_id = ?
+```
+
+**用途**: 获取订单ID列表,用于后续删除验券记录
+
+---
+
+### 6. 删除验券记录(订单-优惠券中间表)
+
+```sql
+DELETE FROM order_coupon_middle
+WHERE order_id IN (订单ID列表)
+```
+
+**说明**: 
+- 必须先查询订单ID,因为 `order_coupon_middle` 表没有 `store_id` 字段
+- 通过 `order_id` 关联删除
+
+---
+
+### 7. 删除订单记录
+
+```sql
+DELETE FROM life_user_order
+WHERE store_id = ?
+```
+
+**影响范围**:
+- 团购券订单
+- 代金券订单
+- 会员卡订单
+- 所有订单状态(待支付、已支付、已使用、已退款等)
+
+---
+
+### 8. 删除团购券
+
+```sql
+DELETE FROM life_coupon
+WHERE store_id = ?
+```
+
+**影响范围**:
+- 店铺发布的所有团购券
+- 包括上架、下架、已售罄等所有状态
+
+---
+
+### 9. 删除代金券
+
+```sql
+DELETE FROM life_discount_coupon
+WHERE store_id = ?
+```
+
+**影响范围**:
+- 店铺发布的所有代金券
+- 包括所有状态的代金券
+
+---
+
+### 10. 删除用户领取的优惠券
+
+```sql
+DELETE FROM life_discount_coupon_user
+WHERE user_id = ?
+```
+
+**说明**: 删除商户用户本人领取的优惠券
+
+---
+
+### 11. 清理所有通知消息
+
+```sql
+DELETE FROM life_notice
+WHERE receiver_id = ?
+```
+
+**receiver_id 格式**: `store_{手机号}`
+
+**影响范围**:
+- 入住申请通知
+- 订单通知
+- 优惠券通知
+- 系统通知
+- 所有其他通知
+
+---
+
+### 12. 重置用户信息
+
+```sql
+UPDATE store_user
+SET store_id = NULL,
+    name = NULL,
+    id_card = NULL,
+    alipay_account = NULL
+WHERE id = ?
+```
+
+**技术实现**:
+```java
+// 使用 LambdaUpdateWrapper 显式设置为 null
+LambdaUpdateWrapper<StoreUser> updateWrapper = new LambdaUpdateWrapper<>();
+updateWrapper.eq(StoreUser::getId, userId)
+        .set(StoreUser::getStoreId, null)
+        .set(StoreUser::getName, null)
+        .set(StoreUser::getIdCard, null)
+        .set(StoreUser::getAlipayAccount, null);
+storeUserMapper.update(null, updateWrapper);
+```
+
+**为什么不能用 `updateById`?**
+- ❌ `updateById(entity)` 默认会忽略 null 值
+- ✅ 必须使用 `UpdateWrapper.set()` 显式设置为 null
+- ✅ 调用 `update(null, wrapper)` 来执行更新
+
+**字段说明**:
+- `store_id`: 解绑店铺关联
+- `name`: 清空联系人姓名(入住时填写)
+- `id_card`: 清空身份证号(入住时填写)
+- `alipay_account`: 清空支付宝账号(入住时填写)
+
+**保留字段**:
+- `phone`: 手机号(注册信息)
+- `head_img`: 头像
+- `money`: 账户余额
+- `pay_password`: 支付密码
+
+---
+
+## 业务场景
+
+### 场景 1: 正常重置(已申请入住)
+
+**前置条件**:
+- 用户已注册
+- 已提交入住申请
+- storeId 不为空
+
+**请求**:
+```http
+POST /merchantUser/resetToInitialStatus
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": true,
+    "msg": "重置成功,已退回到刚注册状态"
+}
+```
+
+**数据库变化**:
+- ❌ 删除 store_info 记录
+- ❌ 删除 store_img 记录
+- ❌ 删除 tag_store_relation 记录
+- ❌ 删除 life_notice 记录(入住申请)
+- ❌ 删除 life_user_order 记录(所有订单)
+- ❌ 删除 order_coupon_middle 记录(验券记录)
+- ❌ 删除 life_coupon 记录(团购券)
+- ❌ 删除 life_discount_coupon 记录(代金券)
+- ❌ 删除 life_discount_coupon_user 记录(用户优惠券)
+- ❌ 删除所有 life_notice 记录(所有通知)
+- ✅ store_user.store_id = NULL
+- ✅ store_user.name = NULL
+- ✅ store_user.id_card = NULL
+- ✅ store_user.alipay_account = NULL
+
+---
+
+### 场景 2: 重置(未申请入住)
+
+**前置条件**:
+- 用户已注册
+- 未提交入住申请
+- storeId 为空
+
+**请求**:
+```http
+POST /merchantUser/resetToInitialStatus
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": true,
+    "msg": "重置成功,已退回到刚注册状态"
+}
+```
+
+**说明**: 用户未关联店铺,无需清理,直接返回成功。
+
+---
+
+### 场景 3: 未登录访问
+
+**请求**:
+```http
+POST /merchantUser/resetToInitialStatus
+```
+
+**响应**:
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "请先登录"
+}
+```
+
+---
+
+## 前端集成示例
+
+### Vue.js 示例
+
+```javascript
+export default {
+    methods: {
+        async resetToInitialStatus() {
+            // 1. 确认提示
+            const confirmed = await this.$confirm(
+                '此操作将删除所有入住申请数据,恢复到刚注册状态,是否继续?',
+                '确认重置',
+                {
+                    confirmButtonText: '确定',
+                    cancelButtonText: '取消',
+                    type: 'warning'
+                }
+            ).catch(() => false);
+            
+            if (!confirmed) {
+                return;
+            }
+            
+            // 2. 发送请求
+            try {
+                const response = await this.$axios.post('/merchantUser/resetToInitialStatus');
+                
+                if (response.data.success) {
+                    this.$message.success('重置成功');
+                    // 刷新页面或跳转
+                    this.$router.push('/register-success');
+                } else {
+                    this.$message.error(response.data.msg);
+                }
+            } catch (error) {
+                console.error('重置失败:', error);
+                this.$message.error('重置失败,请稍后重试');
+            }
+        }
+    }
+}
+```
+
+---
+
+### React 示例
+
+```javascript
+import { useState } from 'react';
+import axios from 'axios';
+import { Modal } from 'antd';
+
+function ResetButton() {
+    const [loading, setLoading] = useState(false);
+    
+    const handleReset = () => {
+        Modal.confirm({
+            title: '确认重置',
+            content: '此操作将删除所有入住申请数据,恢复到刚注册状态,是否继续?',
+            okText: '确定',
+            cancelText: '取消',
+            okType: 'danger',
+            onOk: async () => {
+                setLoading(true);
+                try {
+                    const response = await axios.post('/merchantUser/resetToInitialStatus');
+                    
+                    if (response.data.success) {
+                        alert('重置成功');
+                        // 刷新或跳转
+                        window.location.href = '/register-success';
+                    } else {
+                        alert(response.data.msg);
+                    }
+                } catch (error) {
+                    console.error('重置失败:', error);
+                    alert('重置失败,请稍后重试');
+                } finally {
+                    setLoading(false);
+                }
+            }
+        });
+    };
+    
+    return (
+        <button onClick={handleReset} disabled={loading}>
+            {loading ? '重置中...' : '重置到刚注册状态'}
+        </button>
+    );
+}
+```
+
+---
+
+## 测试用例
+
+### 测试场景1: 正常重置
+
+**前置条件**:
+- 用户已登录
+- 已申请入住
+- store_id 不为空
+
+**执行步骤**:
+1. 提交重置请求
+2. 检查响应
+
+**预期结果**:
+- 返回成功
+- store_info 记录被删除
+- store_img 记录被删除
+- tag_store_relation 记录被删除
+- life_notice 相关记录被删除
+- store_user.store_id = NULL
+- store_user.name = NULL
+- store_user.id_card = NULL
+
+---
+
+### 测试场景2: 未入住用户重置
+
+**前置条件**:
+- 用户已登录
+- 未申请入住
+- store_id 为空
+
+**执行步骤**:
+1. 提交重置请求
+
+**预期结果**:
+- 返回成功
+- 无数据删除操作
+
+---
+
+### 测试场景3: 事务回滚测试
+
+**前置条件**:
+- 模拟数据库异常
+
+**执行步骤**:
+1. 提交重置请求
+2. 在删除过程中触发异常
+
+**预期结果**:
+- 返回失败
+- 所有数据操作回滚
+- 数据库状态不变
+
+---
+
+## 常见问题
+
+### Q1: 重置后哪些数据会被保留?
+
+**答案**: 
+- ✅ **保留**: 手机号、头像、账户余额、支付密码
+- ❌ **清除**: 店铺关联、入住申请数据、店铺图片、订单、优惠券、验券记录、通知消息、支付宝账号
+
+---
+
+### Q2: 重置操作会影响账户余额吗?
+
+**答案**: 不会。账户余额、支付密码等财务相关信息不受影响。
+
+---
+
+### Q3: 为什么 `updateById` 无法将字段设置为 null?
+
+**答案**: 
+- ❌ MyBatis-Plus 的 `updateById(entity)` 默认会**忽略 null 值**
+- ✅ 必须使用 `LambdaUpdateWrapper.set(field, null)` 显式设置
+- ✅ 正确用法:
+```java
+LambdaUpdateWrapper<StoreUser> wrapper = new LambdaUpdateWrapper<>();
+wrapper.eq(StoreUser::getId, userId)
+       .set(StoreUser::getStoreId, null)
+       .set(StoreUser::getName, null);
+storeUserMapper.update(null, wrapper);
+```
+
+---
+
+### Q4: 重置后可以重新申请入住吗?
+
+**答案**: 可以。重置后用户状态恢复到刚注册时,可以重新提交入住申请。
+
+---
+
+### Q5: 重置操作是否支持事务回滚?
+
+**答案**: 支持。使用 `@Transactional` 注解,任何异常都会导致所有操作回滚。
+
+---
+
+### Q6: Redis 中的地理位置信息会被清理吗?
+
+**答案**: 不会自动清理。如需清理 Redis 数据,需要额外调用相关清理接口。
+
+---
+
+### Q7: 多次重置会有问题吗?
+
+**答案**: 不会。第一次重置后,storeId 已为空,后续重置会直接返回成功,不会执行删除操作。
+
+---
+
+### Q8: 支付宝账号会被清除吗?
+
+**答案**: 
+- ✅ **v1.2 及以上**: 会被清除(入住时可能填写的支付宝账号)
+- ❌ **v1.1 及以下**: 不会被清除
+
+---
+
+## 注意事项
+
+### 1. 数据不可恢复
+
+- ⚠️ **重要**: 重置操作会物理删除数据,不可恢复
+- ⚠️ **建议**: 仅在测试环境或确需重新申请时使用
+- ⚠️ **生产环境**: 谨慎使用,建议增加二次确认
+
+### 2. 事务保护
+
+- ✅ 使用 `@Transactional` 保证原子性
+- ✅ 任何步骤失败都会回滚
+- ✅ 保证数据一致性
+
+### 3. 权限控制
+
+- ⚠️ 需要登录验证
+- ⚠️ 只能重置自己的数据
+- ⚠️ 无法重置他人账户
+
+### 4. MyBatis-Plus 注意事项
+
+- ⚠️ **不能使用 `updateById` 设置 null 值**
+- ✅ 必须使用 `LambdaUpdateWrapper.set(field, null)`
+- ✅ 调用 `update(null, wrapper)` 执行更新
+
+### 5. Redis 数据
+
+- ⚠️ Redis 地理位置信息需额外清理
+- ⚠️ 可能需要调用其他清理接口
+
+### 6. 业务影响
+
+- ⚠️ 清理所有入住申请数据
+- ⚠️ 清理所有订单、优惠券、验券记录
+- ⚠️ 解绑店铺关联
+- ⚠️ 清理个人信息(姓名、身份证、支付宝账号)
+
+---
+
+## 安全建议
+
+### 1. 二次确认
+
+```java
+// 前端实现二次确认
+const confirmed = await this.$confirm('确认重置?', '警告', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+});
+```
+
+### 2. 操作日志
+
+```java
+// 建议记录重置操作日志
+OperationLog log = new OperationLog();
+log.setUserId(userId);
+log.setOperation("RESET_TO_INITIAL_STATUS");
+log.setDetails("重置商户到刚注册状态");
+log.setIp(request.getRemoteAddr());
+operationLogMapper.insert(log);
+```
+
+### 3. 权限限制
+
+```java
+// 生产环境建议添加角色限制
+@PreAuthorize("hasRole('ADMIN') or hasRole('TEST_USER')")
+public boolean resetToInitialStatus()
+```
+
+---
+
+## 清理范围汇总
+
+### 会被清理的数据
+
+| 表名 | 清理条件 | 说明 |
+|------|---------|------|
+| store_info | id = storeId | 店铺信息 |
+| store_img | store_id = storeId AND img_type IN (14,15,25) | 入住图片 |
+| tag_store_relation | store_id = storeId | 店铺标签 |
+| life_notice | receiver_id = 'store_{phone}' | 所有通知消息 |
+| life_user_order | store_id = storeId | 所有订单记录 |
+| order_coupon_middle | order_id IN (订单ID列表) | 验券记录 |
+| life_coupon | store_id = storeId | 团购券 |
+| life_discount_coupon | store_id = storeId | 代金券 |
+| life_discount_coupon_user | user_id = userId | 用户优惠券 |
+| store_user | store_id/name/id_card/alipay_account 设为NULL | 解绑并清空 |
+
+### 不会被清理的数据
+
+| 表名 | 保留字段 | 说明 |
+|------|---------|------|
+| store_user | phone | 手机号 |
+| store_user | head_img | 头像 |
+| store_user | money | 账户余额 |
+| store_user | pay_password | 支付密码 |
+| Redis | geo:stores | 地理位置(需手动清理) |
+
+---
+
+## 更新日志
+
+### 2025-11-21 (v1.2)
+
+**Bug 修复**:
+- 🐛 修复 `updateById` 无法将字段设置为 null 的问题
+- ✅ 改用 `LambdaUpdateWrapper.set(field, null)` 显式设置
+- ✅ 正确清空 storeId、name、idCard、alipayAccount
+
+**功能优化**:
+- ✅ 新增清空支付宝账号(alipayAccount)
+- ✅ 确保所有字段都能正确设置为 null
+
+**技术说明**:
+```java
+// ❌ 错误用法(不生效)
+StoreUser user = new StoreUser();
+user.setId(userId);
+user.setStoreId(null);
+storeUserMapper.updateById(user);  // null 值会被忽略
+
+// ✅ 正确用法
+LambdaUpdateWrapper<StoreUser> wrapper = new LambdaUpdateWrapper<>();
+wrapper.eq(StoreUser::getId, userId)
+       .set(StoreUser::getStoreId, null);  // 显式设置为 null
+storeUserMapper.update(null, wrapper);
+```
+
+---
+
+### 2025-11-21 (v1.1)
+
+**功能增强**:
+- ✅ 新增订单数据清理(life_user_order)
+- ✅ 新增验券记录清理(order_coupon_middle)
+- ✅ 新增团购券清理(life_coupon)
+- ✅ 新增代金券清理(life_discount_coupon)
+- ✅ 新增用户优惠券清理(life_discount_coupon_user)
+- ✅ 增强通知清理(清理所有通知,不仅限入住申请)
+
+**清理范围扩展**:
+- 从原来的 **5 个表** 扩展到 **10 个表**
+- 覆盖入住申请 + 业务运营全流程数据
+
+**技术优化**:
+- ✅ 先查询订单ID列表,再删除验券记录(避免直接关联删除错误)
+- ✅ 优化删除顺序,避免外键约束问题
+- ✅ 详细日志记录每个步骤的删除数量
+
+---
+
+### 2025-11-20 (v1.0)
+
+**新增接口**:
+- ✅ `POST /merchantUser/resetToInitialStatus` - 重置商户到刚注册状态
+
+**核心功能**:
+- ✅ 删除店铺信息(store_info)
+- ✅ 删除店铺图片(store_img: 营业执照、合同、经营许可证)
+- ✅ 删除店铺标签(tag_store_relation)
+- ✅ 清理入住通知(life_notice)
+- ✅ 重置用户信息(store_user: storeId、name、idCard)
+- ✅ 事务保护(@Transactional)
+- ✅ 详细日志记录
+- ✅ 完善异常处理
+
+**适用场景**:
+- ✅ 测试环境快速重置
+- ✅ 商户需要重新申请入住
+- ✅ 入住申请数据清理
+
+**涉及文件**:
+- `MerchantUserController.java` - 新增 `resetToInitialStatus` 接口
+- `MerchantUserService.java` - 新增方法定义
+- `MerchantUserServiceImpl.java` - 新增方法实现
+
+**代码质量**:
+- ✅ Linter检查:无错误
+- ✅ 日志记录:详细
+- ✅ 异常处理:完善
+- ✅ 代码注释:完整
+- ✅ 事务管理:正确
+
+**开发人员**: ssk
+
+---
+
+**文档版本**: v1.2  
+**最后更新**: 2025-11-21  
+**维护人员**: ssk
+

+ 790 - 0
alien-store-platform/接口文档/30-已入账未入账详情查询接口.md

@@ -0,0 +1,790 @@
+# Web端商户已入账/未入账详情查询接口文档
+
+## 模块概述
+
+本接口用于查询商户的收入明细,支持查询已入账和未入账的收益详情。可以按时间范围、收入类型进行筛选,并提供分页功能。
+
+---
+
+## 接口信息
+
+### 已入账/未入账详情查询
+
+#### 接口详情
+
+- **接口名称**: 已入账/未入账详情查询
+- **接口路径**: `GET /incomeManage/getPaymentDetails`
+- **请求方式**: GET
+- **接口描述**: 查询商户的收入明细,区分已入账和未入账状态
+- **登录验证**: ✅ 需要(通过 JWT token 自动获取店铺ID)
+- **数据来源**: app端 `/alienStore/storeIncomeDetailsRecord/noYetPayment` 接口
+
+---
+
+## 业务逻辑说明
+
+### 入账规则
+
+#### **未入账 (paymentType=0)**:
+- **时间范围**: 当前时间-3天内的记录
+- **条件**: `cash_out_id IS NULL`(未绑定提现记录)
+- **说明**: 3天内的收入还未到账期,不能提现
+
+#### **已入账 (paymentType=1)**:
+- **时间范围**: 当前时间-4天 至 当前时间-27天的记录
+- **条件**: 创建时间在4~27天之间
+- **说明**: 4-27天前的收入已到账期,可以提现
+
+### 收入类型说明
+
+| incomeType | 说明 | 备注 |
+|-----------|------|------|
+| 0 | 主页 | 包含优惠券(1)和团购券(2) |
+| 1 | 优惠券 | 单独查询优惠券收入 |
+| 2 | 代金券 | 单独查询代金券收入 |
+| 3 | 套餐 | 单独查询套餐收入 |
+| 4 | 联名卡 | 单独查询联名卡收入 |
+
+---
+
+## 请求参数
+
+### Query 参数
+
+| 参数名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| incomeType | Integer | 否 | - | 收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡 |
+| paymentType | Integer | 是 | - | 账期类型, 0:未入账, 1:已入账 |
+| startTime | String | 是 | - | 开始时间(格式:yyyy-MM-dd) |
+| endTime | String | 是 | - | 结束时间(格式:yyyy-MM-dd) |
+| page | Integer | 否 | 1 | 页码(从1开始) |
+| size | Integer | 否 | 10 | 每页条数 |
+
+**说明**:
+- `storeId` 从 JWT token 自动获取,无需传参
+- `startTime` 和 `endTime` 用于自定义查询时间范围
+- 系统会根据 `paymentType` 自动计算默认时间范围
+
+---
+
+## 请求示例
+
+### 示例1: 查询未入账收入
+
+```http
+GET /incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
+Headers:
+  Authorization: Bearer YOUR_JWT_TOKEN
+```
+
+```bash
+curl -X GET "http://localhost:8080/incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10" \
+  -H "Authorization: Bearer YOUR_JWT_TOKEN"
+```
+
+---
+
+### 示例2: 查询已入账的优惠券收入
+
+```http
+GET /incomeManage/getPaymentDetails?incomeType=1&paymentType=1&startTime=2025-10-01&endTime=2025-11-11&page=1&size=10
+Headers:
+  Authorization: Bearer YOUR_JWT_TOKEN
+```
+
+```bash
+curl -X GET "http://localhost:8080/incomeManage/getPaymentDetails?incomeType=1&paymentType=1&startTime=2025-10-01&endTime=2025-11-11&page=1&size=10" \
+  -H "Authorization: Bearer YOUR_JWT_TOKEN"
+```
+
+---
+
+### 示例3: 查询主页(优惠券+团购券)未入账收入
+
+```http
+GET /incomeManage/getPaymentDetails?incomeType=0&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
+Headers:
+  Authorization: Bearer YOUR_JWT_TOKEN
+```
+
+---
+
+## 响应参数
+
+### 成功响应
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "date": "2025-11-08 ~ 2025-11-11",
+        "money": "128.50",
+        "data": {
+            "records": [
+                {
+                    "id": 1001,
+                    "storeId": 103,
+                    "money": 5000,
+                    "moneyStr": "50.00",
+                    "commission": 150,
+                    "commissionStr": "1.50",
+                    "incomeMoney": "51.50",
+                    "incomeType": 1,
+                    "couponName": "50元优惠券",
+                    "cashOutId": null,
+                    "date": "2025-11-10",
+                    "commissionRate": "3",
+                    "createdTime": "2025-11-10 14:30:00"
+                },
+                {
+                    "id": 1002,
+                    "storeId": 103,
+                    "money": 7800,
+                    "moneyStr": "78.00",
+                    "commission": 234,
+                    "commissionStr": "2.34",
+                    "incomeMoney": "80.34",
+                    "incomeType": 2,
+                    "couponName": "100元团购券",
+                    "cashOutId": null,
+                    "date": "2025-11-09",
+                    "commissionRate": "3",
+                    "createdTime": "2025-11-09 16:20:00"
+                }
+            ],
+            "total": 25,
+            "size": 10,
+            "current": 1,
+            "pages": 3
+        }
+    },
+    "msg": "操作成功"
+}
+```
+
+### 响应字段说明
+
+#### 顶层字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| code | Integer | 状态码(200-成功) |
+| success | Boolean | 是否成功 |
+| data | JSONObject | 返回数据 |
+| msg | String | 提示信息 |
+
+#### data 字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| date | String | 账期时间范围 |
+| money | String | 总金额(元,保留2位小数) |
+| data | Page | 分页数据 |
+
+#### data.data 分页字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| records | Array | 收入明细列表 |
+| total | Integer | 总记录数 |
+| size | Integer | 每页条数 |
+| current | Integer | 当前页码 |
+| pages | Integer | 总页数 |
+
+#### records 明细字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | Integer | 收入记录ID |
+| storeId | Integer | 店铺ID |
+| money | Integer | 收益金额(分) |
+| moneyStr | String | 收益金额(元,格式化) |
+| commission | Integer | 手续费(分) |
+| commissionStr | String | 手续费(元,格式化) |
+| incomeMoney | String | 售价(收益+手续费,元) |
+| incomeType | Integer | 收入类型 |
+| couponName | String | 优惠券名称 |
+| cashOutId | Integer | 提现记录ID(null表示未提现) |
+| date | String | 日期(yyyy-MM-dd) |
+| commissionRate | String | 抽成比例 |
+| createdTime | String | 创建时间 |
+
+---
+
+### 失败响应
+
+#### 1. 未登录或 token 无效
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "请先登录"
+}
+```
+
+#### 2. 参数错误
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "查询失败:时间格式错误"
+}
+```
+
+#### 3. 查询异常
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "查询失败:{异常信息}"
+}
+```
+
+---
+
+## 业务场景
+
+### 场景 1: 查询未入账收入(近3天)
+
+**业务需求**: 商户想查看最近3天内产生的收入,这些收入还未到提现账期
+
+**请求**:
+```http
+GET /incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-08&endTime=2025-11-11&page=1&size=10
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "date": "2025-11-08 ~ 2025-11-11",
+        "money": "256.80",
+        "data": {
+            "records": [...],
+            "total": 15,
+            "current": 1,
+            "pages": 2
+        }
+    }
+}
+```
+
+**业务逻辑**:
+- ✅ 查询 `created_time > (当前时间 - 3天)` 的记录
+- ✅ 且 `cash_out_id IS NULL`(未绑定提现)
+- ✅ 按创建时间倒序排列
+- ✅ 返回汇总金额和明细列表
+
+---
+
+### 场景 2: 查询已入账收入(4-27天前)
+
+**业务需求**: 商户想查看可以提现的收入,这些收入已到账期
+
+**请求**:
+```http
+GET /incomeManage/getPaymentDetails?paymentType=1&startTime=2025-10-15&endTime=2025-11-07&page=1&size=10
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "date": "2025-10-15 ~ 2025-11-07",
+        "money": "1580.50",
+        "data": {
+            "records": [...],
+            "total": 42,
+            "current": 1,
+            "pages": 5
+        }
+    }
+}
+```
+
+**业务逻辑**:
+- ✅ 查询 `created_time BETWEEN (当前时间-27天) AND (当前时间-4天)` 的记录
+- ✅ 按创建时间倒序排列
+- ✅ 这些收入可以申请提现
+
+---
+
+### 场景 3: 按收入类型筛选(优惠券)
+
+**业务需求**: 商户只想查看优惠券产生的收入
+
+**请求**:
+```http
+GET /incomeManage/getPaymentDetails?incomeType=1&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "date": "2025-11-08 ~ 2025-11-11",
+        "money": "86.20",
+        "data": {
+            "records": [
+                {
+                    "incomeType": 1,
+                    "couponName": "50元优惠券",
+                    "moneyStr": "50.00",
+                    ...
+                }
+            ],
+            "total": 5,
+            "current": 1,
+            "pages": 1
+        }
+    }
+}
+```
+
+---
+
+### 场景 4: 查询主页收入(优惠券+团购券)
+
+**业务需求**: 商户想查看主页展示的收入汇总(包含优惠券和团购券)
+
+**请求**:
+```http
+GET /incomeManage/getPaymentDetails?incomeType=0&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "date": "2025-11-08 ~ 2025-11-11",
+        "money": "328.60",
+        "data": {
+            "records": [
+                {"incomeType": 1, "couponName": "50元优惠券", ...},
+                {"incomeType": 2, "couponName": "100元团购券", ...}
+            ],
+            "total": 18,
+            "current": 1,
+            "pages": 2
+        }
+    }
+}
+```
+
+**业务逻辑**:
+- ✅ `incomeType=0` 会自动查询 `income_type IN (1, 2)`
+- ✅ 包含优惠券和团购券的收入
+
+---
+
+## 数据库查询逻辑
+
+### SQL 示例(未入账)
+
+```sql
+SELECT sidr.*
+FROM store_income_details_record sidr
+WHERE sidr.store_id = ?
+  AND sidr.cash_out_id IS NULL
+  AND sidr.created_time > DATE_SUB(NOW(), INTERVAL 3 DAY)
+  AND sidr.created_time BETWEEN ? AND ?
+  AND sidr.income_type IN (1, 2)  -- 如果 incomeType=0
+ORDER BY sidr.created_time DESC
+```
+
+---
+
+### SQL 示例(已入账)
+
+```sql
+SELECT sidr.*
+FROM store_income_details_record sidr
+WHERE sidr.store_id = ?
+  AND sidr.created_time BETWEEN DATE_SUB(NOW(), INTERVAL 27 DAY) 
+                        AND DATE_SUB(NOW(), INTERVAL 4 DAY)
+  AND sidr.created_time BETWEEN ? AND ?
+  AND sidr.income_type = ?
+ORDER BY sidr.created_time DESC
+```
+
+---
+
+## 金额计算逻辑
+
+### 单条记录计算
+
+```java
+// 1. 收益(分转元)
+moneyStr = money / 100  // 例: 5000分 = 50.00元
+
+// 2. 手续费(分转元)
+commissionStr = commission / 100  // 例: 150分 = 1.50元
+
+// 3. 售价(收益 + 手续费)
+incomeMoney = (money + commission) / 100  // 例: (5000 + 150) / 100 = 51.50元
+```
+
+### 汇总计算
+
+```java
+// 所有记录的收益总和(分转元)
+totalMoney = SUM(money) / 100
+```
+
+---
+
+## 前端集成示例
+
+### Vue.js 示例
+
+```javascript
+export default {
+    data() {
+        return {
+            paymentType: 0,  // 0-未入账, 1-已入账
+            incomeType: null,
+            startTime: '2025-11-09',
+            endTime: '2025-11-11',
+            page: 1,
+            size: 10,
+            loading: false,
+            tableData: [],
+            totalMoney: '0.00',
+            dateRange: '',
+            total: 0
+        };
+    },
+    methods: {
+        async fetchPaymentDetails() {
+            this.loading = true;
+            try {
+                const params = {
+                    paymentType: this.paymentType,
+                    startTime: this.startTime,
+                    endTime: this.endTime,
+                    page: this.page,
+                    size: this.size
+                };
+                
+                if (this.incomeType !== null) {
+                    params.incomeType = this.incomeType;
+                }
+                
+                const response = await this.$axios.get('/incomeManage/getPaymentDetails', { params });
+                
+                if (response.data.success) {
+                    const data = response.data.data;
+                    this.tableData = data.data.records;
+                    this.totalMoney = data.money;
+                    this.dateRange = data.date;
+                    this.total = data.data.total;
+                    this.$message.success('查询成功');
+                } else {
+                    this.$message.error(response.data.msg);
+                }
+            } catch (error) {
+                console.error('查询失败:', error);
+                this.$message.error('查询失败,请稍后重试');
+            } finally {
+                this.loading = false;
+            }
+        },
+        
+        handlePageChange(page) {
+            this.page = page;
+            this.fetchPaymentDetails();
+        },
+        
+        switchPaymentType(type) {
+            this.paymentType = type;
+            this.page = 1;
+            this.fetchPaymentDetails();
+        }
+    },
+    mounted() {
+        this.fetchPaymentDetails();
+    }
+}
+```
+
+---
+
+### React 示例
+
+```javascript
+import { useState, useEffect } from 'react';
+import axios from 'axios';
+import { Table, Radio, DatePicker, Select } from 'antd';
+
+function PaymentDetails() {
+    const [paymentType, setPaymentType] = useState(0);
+    const [incomeType, setIncomeType] = useState(null);
+    const [dateRange, setDateRange] = useState(['2025-11-09', '2025-11-11']);
+    const [page, setPage] = useState(1);
+    const [size, setSize] = useState(10);
+    const [loading, setLoading] = useState(false);
+    const [tableData, setTableData] = useState([]);
+    const [totalMoney, setTotalMoney] = useState('0.00');
+    const [total, setTotal] = useState(0);
+    
+    const fetchData = async () => {
+        setLoading(true);
+        try {
+            const params = {
+                paymentType,
+                startTime: dateRange[0],
+                endTime: dateRange[1],
+                page,
+                size
+            };
+            
+            if (incomeType !== null) {
+                params.incomeType = incomeType;
+            }
+            
+            const response = await axios.get('/incomeManage/getPaymentDetails', { params });
+            
+            if (response.data.success) {
+                const data = response.data.data;
+                setTableData(data.data.records);
+                setTotalMoney(data.money);
+                setTotal(data.data.total);
+            }
+        } catch (error) {
+            console.error('查询失败:', error);
+        } finally {
+            setLoading(false);
+        }
+    };
+    
+    useEffect(() => {
+        fetchData();
+    }, [paymentType, dateRange, page, incomeType]);
+    
+    return (
+        <div>
+            <Radio.Group value={paymentType} onChange={e => setPaymentType(e.target.value)}>
+                <Radio.Button value={0}>未入账</Radio.Button>
+                <Radio.Button value={1}>已入账</Radio.Button>
+            </Radio.Group>
+            
+            <Select 
+                value={incomeType} 
+                onChange={setIncomeType}
+                placeholder="选择收入类型"
+                allowClear
+            >
+                <Select.Option value={0}>主页</Select.Option>
+                <Select.Option value={1}>优惠券</Select.Option>
+                <Select.Option value={2}>代金券</Select.Option>
+                <Select.Option value={3}>套餐</Select.Option>
+                <Select.Option value={4}>联名卡</Select.Option>
+            </Select>
+            
+            <div>总金额: ¥{totalMoney}</div>
+            
+            <Table 
+                dataSource={tableData}
+                loading={loading}
+                pagination={{
+                    current: page,
+                    pageSize: size,
+                    total: total,
+                    onChange: setPage
+                }}
+            />
+        </div>
+    );
+}
+```
+
+---
+
+## 测试用例
+
+### 测试场景1: 查询未入账收入
+
+**前置条件**:
+- 用户已登录
+- 店铺ID=103
+- 近3天有5笔收入记录
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10`
+
+**预期结果**:
+- 返回成功
+- `date` = "2025-11-08 ~ 2025-11-11"
+- `money` = 所有记录的收益总和
+- `data.total` = 5
+- 所有记录的 `cashOutId` = null
+
+---
+
+### 测试场景2: 查询已入账收入
+
+**前置条件**:
+- 用户已登录
+- 4-27天前有10笔收入记录
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getPaymentDetails?paymentType=1&startTime=2025-10-15&endTime=2025-11-07&page=1&size=10`
+
+**预期结果**:
+- 返回成功
+- `date` = "2025-10-15 ~ 2025-11-07"
+- `data.total` = 10
+- 记录的创建时间在4-27天之间
+
+---
+
+### 测试场景3: 按收入类型筛选
+
+**前置条件**:
+- 近3天有优惠券收入3笔、团购券收入2笔
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getPaymentDetails?incomeType=1&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10`
+
+**预期结果**:
+- 返回成功
+- 只返回优惠券收入(incomeType=1)
+- `data.total` = 3
+
+---
+
+## 常见问题
+
+### Q1: 未入账和已入账的时间范围是固定的吗?
+
+**答案**: 是的。
+- **未入账**: 固定为当前时间-3天内
+- **已入账**: 固定为当前时间-4天至-27天
+- `startTime` 和 `endTime` 用于进一步筛选时间范围
+
+---
+
+### Q2: incomeType=0 和不传 incomeType 有什么区别?
+
+**答案**:
+- **incomeType=0**: 查询优惠券(1)和团购券(2)的收入
+- **不传**: 查询所有类型的收入
+
+---
+
+### Q3: money 和 incomeMoney 有什么区别?
+
+**答案**:
+- **money**: 商户实际收益(已扣除手续费)
+- **incomeMoney**: 售价(收益 + 手续费)
+- 关系: `incomeMoney = money + commission`
+
+---
+
+### Q4: 为什么有些记录有 cashOutId,有些没有?
+
+**答案**:
+- **有 cashOutId**: 已绑定提现记录,收益已申请提现
+- **无 cashOutId**: 未绑定提现记录,收益还未提现
+
+---
+
+### Q5: 分页查询时,total 是指什么?
+
+**答案**: `total` 是符合查询条件的总记录数,不是总页数。
+- 总页数 = `pages` 字段
+- 当前页 = `current` 字段
+
+---
+
+## 注意事项
+
+### 1. 时间范围理解
+
+- ⚠️ 系统自动根据 `paymentType` 计算默认时间范围
+- ⚠️ `startTime` 和 `endTime` 用于进一步筛选
+- ⚠️ 实际查询是两个时间范围的交集
+
+### 2. 金额单位
+
+- ⚠️ 数据库存储单位:**分**
+- ⚠️ 接口返回单位:**元**(保留2位小数)
+- ⚠️ 转换公式:元 = 分 / 100
+
+### 3. 收入类型
+
+- ⚠️ `incomeType=0` 是特殊值,表示主页(优惠券+团购券)
+- ⚠️ 其他值直接对应具体的收入类型
+- ⚠️ 不传表示查询所有类型
+
+### 4. 分页注意
+
+- ⚠️ 使用内存分页(ListToPage)
+- ⚠️ 先查询所有符合条件的记录,再分页
+- ⚠️ 数据量大时可能影响性能
+
+---
+
+## 更新日志
+
+### 2025-11-21 (v1.0)
+
+**新增接口**:
+- ✅ `GET /incomeManage/getPaymentDetails` - 已入账/未入账详情查询
+
+**核心功能**:
+- ✅ 区分未入账(3天内)和已入账(4-27天)
+- ✅ 支持按收入类型筛选
+- ✅ 支持自定义时间范围
+- ✅ 支持分页查询
+- ✅ 返回汇总金额和明细列表
+- ✅ 金额自动转换(分转元)
+- ✅ 自动计算售价、手续费
+
+**数据来源**:
+- app端 `/alienStore/storeIncomeDetailsRecord/noYetPayment` 接口
+- 完全复用业务逻辑
+
+**涉及文件**:
+- `IncomeManageController.java` - 新增 `getPaymentDetails` 接口
+- `IncomeManageService.java` - 新增方法定义
+- `IncomeManageServiceImpl.java` - 新增方法实现
+
+**代码质量**:
+- ✅ Linter检查:无错误(1个警告可忽略)
+- ✅ 日志记录:详细
+- ✅ 异常处理:完善
+- ✅ 代码注释:完整
+
+**开发人员**: ssk
+
+---
+
+**文档版本**: v1.0  
+**最后更新**: 2025-11-21  
+**维护人员**: ssk
+
+

+ 725 - 0
alien-store-platform/接口文档/31-收入汇总统计接口.md

@@ -0,0 +1,725 @@
+# Web端商户收入汇总统计接口文档
+
+## 模块概述
+
+本接口提供收入数据的汇总统计功能,一次性返回可提现金额、今日收益、已入账总数、未入账总数四项关键数据,方便前端首页展示。
+
+---
+
+## 接口信息
+
+### 收入汇总统计
+
+#### 接口详情
+
+- **接口名称**: 收入汇总统计
+- **接口路径**: `GET /incomeManage/getIncomeSummary`
+- **请求方式**: GET
+- **接口描述**: 一次性查询可提现金额、今日收益、已入账金额/数量、未入账金额/数量
+- **登录验证**: ✅ 需要(通过 JWT token 自动获取店铺ID)
+- **数据来源**: 复用三个现有接口的业务逻辑
+
+---
+
+## 业务逻辑说明
+
+### 数据来源接口
+
+本接口整合了以下三个app端接口的数据:
+
+| 数据项 | app端接口 | 业务逻辑 |
+|--------|----------|---------|
+| 可提现金额 | `/alienStore/storeIncomeDetailsRecord/accountBalance` | 4-27天内未绑定提现的收入 - 待审核/已通过的提现金额 |
+| 今日收益 | `/alienStore/storeIncomeDetailsRecord/todayIncome` | 当天00:00:00 ~ 23:59:59的收入总和 |
+| 未入账 | `/alienStore/storeIncomeDetailsRecord/noYetPayment?paymentType=0` | 近3天内未绑定提现的收入 |
+| 已入账 | `/alienStore/storeIncomeDetailsRecord/noYetPayment?paymentType=1` | 4-27天内的收入 |
+
+---
+
+### 数据计算规则
+
+#### **1. 可提现金额**
+
+```java
+// 查询4-27天内未绑定提现记录的收入
+cashOutMoney = SUM(money WHERE created_time BETWEEN (NOW() - 27天) AND (NOW() - 4天) 
+                              AND cash_out_id IS NULL)
+
+// 减去待审核和已通过的提现金额
+totalCashOutAmount = SUM(money WHERE payment_status IN (1, 3) AND delete_flag = 0)
+
+// 可提现金额
+cashOutMoney = cashOutMoney - totalCashOutAmount
+```
+
+**说明**:
+- ✅ 只统计4-27天前的收入(已到提现账期)
+- ✅ 减去已申请但未到账的提现金额
+- ✅ payment_status: 1-待审核, 3-已通过
+
+---
+
+#### **2. 今日收益**
+
+```java
+// 查询今天的收入总和
+todayIncome = SUM(money WHERE created_time BETWEEN '今日 00:00:00' AND '今日 23:59:59')
+```
+
+**说明**:
+- ✅ 统计当天所有收入
+- ✅ 包含所有收入类型(优惠券、代金券、团购券等)
+
+---
+
+#### **3. 未入账(近3天)**
+
+```java
+// 查询近3天未绑定提现的收入
+notPaidMoney = SUM(money WHERE created_time > (NOW() - 3天) 
+                              AND cash_out_id IS NULL)
+notPaidCount = COUNT(*)
+```
+
+**说明**:
+- ✅ 统计近3天的收入
+- ✅ 未绑定提现记录
+- ✅ 还未到提现账期
+
+---
+
+#### **4. 已入账(4-27天)**
+
+```java
+// 查询4-27天前的收入
+paidMoney = SUM(money WHERE created_time BETWEEN (NOW() - 27天) AND (NOW() - 4天))
+paidCount = COUNT(*)
+```
+
+**说明**:
+- ✅ 统计4-27天前的收入
+- ✅ 已到提现账期
+- ✅ 可以申请提现
+
+---
+
+## 请求参数
+
+**无参数**
+
+从 JWT token 自动获取店铺ID(`storeId`)
+
+---
+
+## 请求示例
+
+```http
+GET /incomeManage/getIncomeSummary
+Headers:
+  Authorization: Bearer YOUR_JWT_TOKEN
+```
+
+```bash
+curl -X GET "http://localhost:8080/incomeManage/getIncomeSummary" \
+  -H "Authorization: Bearer YOUR_JWT_TOKEN"
+```
+
+---
+
+## 响应参数
+
+### 成功响应
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "balance": "3580.50",
+        "cashOutMoney": "1256.80",
+        "todayIncome": "128.30",
+        "notPaidMoney": "385.60",
+        "notPaidCount": 12,
+        "notPaidDateRange": "2025-11-18 ~ 2025-11-21",
+        "paidMoney": "1580.20",
+        "paidCount": 45,
+        "paidDateRange": "2025-10-25 ~ 2025-11-17"
+    },
+    "msg": "操作成功"
+}
+```
+
+### 响应字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| code | Integer | 状态码(200-成功) |
+| success | Boolean | 是否成功 |
+| data | Object | 汇总数据 |
+| msg | String | 提示信息 |
+
+#### data 字段详细说明
+
+| 字段名 | 类型 | 单位 | 说明 |
+|--------|------|------|------|
+| **balance** | String | 元 | 账户总余额 |
+| **cashOutMoney** | String | 元 | 可提现金额(4-27天内可提现的收入) |
+| **todayIncome** | String | 元 | 今日收益(当天所有收入) |
+| **notPaidMoney** | String | 元 | 未入账金额(近3天的收入) |
+| **notPaidCount** | Integer | 条 | 未入账记录数 |
+| **notPaidDateRange** | String | - | 未入账时间范围 |
+| **paidMoney** | String | 元 | 已入账金额(4-27天的收入) |
+| **paidCount** | Integer | 条 | 已入账记录数 |
+| **paidDateRange** | String | - | 已入账时间范围 |
+
+---
+
+### 失败响应
+
+#### 1. 未登录或 token 无效
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "请先登录"
+}
+```
+
+#### 2. 查询异常
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "查询失败:{异常信息}"
+}
+```
+
+---
+
+## 业务场景
+
+### 场景 1: 首页数据展示
+
+**业务需求**: 商户登录后,首页需要展示收入概况的关键数据
+
+**请求**:
+```http
+GET /incomeManage/getIncomeSummary
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "balance": "3580.50",
+        "cashOutMoney": "1256.80",
+        "todayIncome": "128.30",
+        "notPaidMoney": "385.60",
+        "notPaidCount": 12,
+        "paidMoney": "1580.20",
+        "paidCount": 45
+    }
+}
+```
+
+**前端展示**:
+```
+┌────────────────────────────────────────┐
+│          收入概况                       │
+├────────────────────────────────────────┤
+│  账户余额: ¥3,580.50                   │
+│  可提现金额: ¥1,256.80                 │
+│  今日收益: ¥128.30                     │
+│                                        │
+│  未入账: ¥385.60 (12笔)                │
+│  已入账: ¥1,580.20 (45笔)              │
+└────────────────────────────────────────┘
+```
+
+---
+
+### 场景 2: 数据刷新
+
+**业务需求**: 用户下拉刷新或手动刷新收入数据
+
+**请求**:
+```http
+GET /incomeManage/getIncomeSummary
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**业务逻辑**:
+- ✅ 重新计算所有统计数据
+- ✅ 返回最新的收入概况
+- ✅ 前端更新展示
+
+---
+
+### 场景 3: 定时刷新
+
+**业务需求**: 前端每隔一定时间自动刷新数据
+
+**实现方式**:
+```javascript
+setInterval(() => {
+    fetchIncomeSummary();
+}, 60000);  // 每分钟刷新一次
+```
+
+---
+
+## 数据关系说明
+
+### 金额之间的关系
+
+```
+账户余额 (balance)
+    = 历史所有收入 - 已提现金额
+
+可提现金额 (cashOutMoney)
+    = 4-27天内未绑定提现的收入 
+    - 待审核的提现金额 
+    - 已通过的提现金额
+
+今日收益 (todayIncome)
+    = 今天所有收入
+
+未入账 (notPaidMoney)
+    = 近3天未绑定提现的收入
+    (还未到提现账期)
+
+已入账 (paidMoney)
+    = 4-27天的收入
+    (已到提现账期,可以提现)
+```
+
+---
+
+### 时间范围关系
+
+```
+时间轴: ←────────────────────────────────→
+       -27天  -4天  -3天  今天
+
+已入账:  ├─────┤
+         4-27天
+
+未入账:          ├──┤
+                3天内
+
+今日收益:             │
+                    今天
+```
+
+---
+
+## 前端集成示例
+
+### Vue.js 示例
+
+```javascript
+export default {
+    data() {
+        return {
+            summary: {
+                balance: '0.00',
+                cashOutMoney: '0.00',
+                todayIncome: '0.00',
+                notPaidMoney: '0.00',
+                notPaidCount: 0,
+                paidMoney: '0.00',
+                paidCount: 0,
+                notPaidDateRange: '',
+                paidDateRange: ''
+            },
+            loading: false
+        };
+    },
+    methods: {
+        async fetchIncomeSummary() {
+            this.loading = true;
+            try {
+                const response = await this.$axios.get('/incomeManage/getIncomeSummary');
+                
+                if (response.data.success) {
+                    this.summary = response.data.data;
+                    this.$message.success('数据加载成功');
+                } else {
+                    this.$message.error(response.data.msg);
+                }
+            } catch (error) {
+                console.error('查询失败:', error);
+                this.$message.error('数据加载失败,请稍后重试');
+            } finally {
+                this.loading = false;
+            }
+        },
+        
+        // 格式化金额显示
+        formatMoney(amount) {
+            return '¥' + parseFloat(amount).toLocaleString('zh-CN', {
+                minimumFractionDigits: 2,
+                maximumFractionDigits: 2
+            });
+        },
+        
+        // 下拉刷新
+        onRefresh() {
+            this.fetchIncomeSummary();
+        }
+    },
+    mounted() {
+        this.fetchIncomeSummary();
+        
+        // 每分钟自动刷新
+        this.timer = setInterval(() => {
+            this.fetchIncomeSummary();
+        }, 60000);
+    },
+    beforeDestroy() {
+        if (this.timer) {
+            clearInterval(this.timer);
+        }
+    }
+}
+```
+
+---
+
+### React 示例
+
+```javascript
+import { useState, useEffect } from 'react';
+import axios from 'axios';
+import { Card, Statistic, Row, Col, Spin } from 'antd';
+import { WalletOutlined, DollarOutlined, RiseOutlined } from '@ant-design/icons';
+
+function IncomeSummary() {
+    const [summary, setSummary] = useState({
+        balance: '0.00',
+        cashOutMoney: '0.00',
+        todayIncome: '0.00',
+        notPaidMoney: '0.00',
+        notPaidCount: 0,
+        paidMoney: '0.00',
+        paidCount: 0
+    });
+    const [loading, setLoading] = useState(false);
+    
+    const fetchData = async () => {
+        setLoading(true);
+        try {
+            const response = await axios.get('/incomeManage/getIncomeSummary');
+            
+            if (response.data.success) {
+                setSummary(response.data.data);
+            }
+        } catch (error) {
+            console.error('查询失败:', error);
+        } finally {
+            setLoading(false);
+        }
+    };
+    
+    useEffect(() => {
+        fetchData();
+        
+        // 每分钟自动刷新
+        const timer = setInterval(fetchData, 60000);
+        
+        return () => clearInterval(timer);
+    }, []);
+    
+    return (
+        <Spin spinning={loading}>
+            <Row gutter={16}>
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="账户余额"
+                            value={summary.balance}
+                            prefix="¥"
+                            precision={2}
+                            valueStyle={{ color: '#3f8600' }}
+                            suffix={<WalletOutlined />}
+                        />
+                    </Card>
+                </Col>
+                
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="可提现金额"
+                            value={summary.cashOutMoney}
+                            prefix="¥"
+                            precision={2}
+                            valueStyle={{ color: '#cf1322' }}
+                            suffix={<DollarOutlined />}
+                        />
+                    </Card>
+                </Col>
+                
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="今日收益"
+                            value={summary.todayIncome}
+                            prefix="¥"
+                            precision={2}
+                            valueStyle={{ color: '#1890ff' }}
+                            suffix={<RiseOutlined />}
+                        />
+                    </Card>
+                </Col>
+                
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="未入账"
+                            value={summary.notPaidMoney}
+                            prefix="¥"
+                            precision={2}
+                            suffix={`(${summary.notPaidCount}笔)`}
+                        />
+                    </Card>
+                </Col>
+            </Row>
+            
+            <Row gutter={16} style={{ marginTop: 16 }}>
+                <Col span={12}>
+                    <Card>
+                        <Statistic
+                            title="已入账"
+                            value={summary.paidMoney}
+                            prefix="¥"
+                            precision={2}
+                            suffix={`(${summary.paidCount}笔)`}
+                        />
+                        <p style={{ marginTop: 8, color: '#999' }}>
+                            {summary.paidDateRange}
+                        </p>
+                    </Card>
+                </Col>
+            </Row>
+        </Spin>
+    );
+}
+```
+
+---
+
+## 测试用例
+
+### 测试场景1: 正常查询
+
+**前置条件**:
+- 用户已登录
+- 店铺ID=103
+- 有收入记录
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getIncomeSummary`
+
+**预期结果**:
+- 返回成功
+- `balance` > 0
+- `cashOutMoney` >= 0
+- `todayIncome` >= 0
+- `notPaidMoney` >= 0, `notPaidCount` >= 0
+- `paidMoney` >= 0, `paidCount` >= 0
+- 包含时间范围字段
+
+---
+
+### 测试场景2: 无收入记录
+
+**前置条件**:
+- 用户已登录
+- 新店铺,无任何收入
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getIncomeSummary`
+
+**预期结果**:
+- 返回成功
+- 所有金额字段 = "0.00"
+- 所有数量字段 = 0
+
+---
+
+### 测试场景3: 只有今日收入
+
+**前置条件**:
+- 用户已登录
+- 只有今天有收入记录
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getIncomeSummary`
+
+**预期结果**:
+- `todayIncome` > 0
+- `notPaidMoney` > 0(今天的收入也在3天内)
+- `paidMoney` = 0(没有4-27天前的收入)
+
+---
+
+## 常见问题
+
+### Q1: 为什么可提现金额比已入账金额少?
+
+**答案**: 
+- 可提现金额 = 已入账金额 - 待审核的提现 - 已通过的提现
+- 如果有提现申请在审核中或已通过,会从可提现金额中扣除
+
+---
+
+### Q2: 今日收益会计入未入账吗?
+
+**答案**: 会的。
+- 今日收益统计当天所有收入
+- 未入账统计近3天的收入(包括今天)
+- 所以今日收益会包含在未入账中
+
+---
+
+### Q3: 账户余额和可提现金额有什么区别?
+
+**答案**:
+- **账户余额**: 所有历史收入 - 已提现金额(总余额)
+- **可提现金额**: 4-27天内可以立即提现的金额(扣除审核中的提现)
+
+---
+
+### Q4: 已入账的收入一定可以提现吗?
+
+**答案**: 不一定。
+- 已入账 = 4-27天前的收入(已到账期)
+- 可提现金额 = 已入账 - 审核中的提现 - 已通过的提现
+- 如果已有提现申请,可提现金额会相应减少
+
+---
+
+### Q5: 时间范围是如何计算的?
+
+**答案**:
+- **未入账**: 当前时间 - 3天 ~ 当前时间
+- **已入账**: 当前时间 - 27天 ~ 当前时间 - 4天
+- 时间范围会随着查询时间动态变化
+
+---
+
+## 注意事项
+
+### 1. 数据实时性
+
+- ⚠️ 数据基于查询时刻实时计算
+- ⚠️ 建议前端设置定时刷新(如每分钟)
+- ⚠️ 或提供手动刷新按钮
+
+### 2. 金额单位
+
+- ⚠️ 数据库存储单位:**分**
+- ⚠️ 接口返回单位:**元**(保留2位小数)
+- ⚠️ 转换公式:元 = 分 / 100
+
+### 3. 时间范围
+
+- ⚠️ 时间范围是动态的,基于当前时间计算
+- ⚠️ 未入账:3天内
+- ⚠️ 已入账:4-27天
+- ⚠️ 可提现:4-27天内未绑定提现的
+
+### 4. 性能考虑
+
+- ⚠️ 涉及多次数据库查询
+- ⚠️ 建议适当缓存(如Redis,5分钟过期)
+- ⚠️ 避免频繁调用
+
+---
+
+## 性能优化建议
+
+### 1. 缓存方案
+
+```java
+@Cacheable(value = "incomeSummary", key = "#storeId", unless = "#result == null")
+public Map<String, Object> getIncomeSummary(Integer storeId) {
+    // ... 查询逻辑
+}
+```
+
+**配置**:
+```yaml
+spring:
+  cache:
+    type: redis
+    redis:
+      time-to-live: 300000  # 5分钟过期
+```
+
+---
+
+### 2. 异步查询
+
+```java
+// 使用CompletableFuture并行查询
+CompletableFuture<String> balanceFuture = CompletableFuture.supplyAsync(() -> getBalance(storeId));
+CompletableFuture<String> todayIncomeFuture = CompletableFuture.supplyAsync(() -> getTodayIncome(storeId));
+CompletableFuture<Map> notPaidFuture = CompletableFuture.supplyAsync(() -> getNotPaidData(storeId));
+CompletableFuture<Map> paidFuture = CompletableFuture.supplyAsync(() -> getPaidData(storeId));
+
+// 等待所有查询完成
+CompletableFuture.allOf(balanceFuture, todayIncomeFuture, notPaidFuture, paidFuture).join();
+```
+
+---
+
+## 更新日志
+
+### 2025-11-21 (v1.0)
+
+**新增接口**:
+- ✅ `GET /incomeManage/getIncomeSummary` - 收入汇总统计
+
+**核心功能**:
+- ✅ 一次性返回4项关键数据
+- ✅ 账户余额
+- ✅ 可提现金额
+- ✅ 今日收益
+- ✅ 已入账/未入账金额和数量
+
+**数据来源**:
+- 复用三个现有接口的业务逻辑
+- `/accountBalance` - 可提现金额
+- `/todayIncome` - 今日收益
+- `/noYetPayment` - 已入账/未入账
+
+**涉及文件**:
+- `IncomeManageController.java` - 新增 `getIncomeSummary` 接口
+- `IncomeManageService.java` - 新增方法定义
+- `IncomeManageServiceImpl.java` - 新增方法实现
+
+**代码质量**:
+- ✅ Linter检查:无错误
+- ✅ 日志记录:详细
+- ✅ 异常处理:完善
+- ✅ 代码注释:完整
+
+**开发人员**: ssk
+
+---
+
+**文档版本**: v1.0  
+**最后更新**: 2025-11-21  
+**维护人员**: ssk
+
+

+ 15 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -1,6 +1,7 @@
 package shop.alien.store.controller;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
@@ -9,12 +10,15 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoodsCategory;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.StoreInfoDto;
 import shop.alien.entity.store.vo.*;
+import shop.alien.entity.storePlatform.StoreLicenseHistory;
 import shop.alien.mapper.StoreCommentSummaryInterestMapper;
 import shop.alien.mapper.TagsMainMapper;
 import shop.alien.mapper.WebAuditMapper;
+import shop.alien.mapper.storePlantform.StoreLicenseHistoryMapper;
 import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.service.StoreInfoService;
 
@@ -48,6 +52,9 @@ public class StoreInfoController {
 
     private final StoreCommentSummaryInterestMapper storeCommentSummaryInterestMapper;
 
+    /** 商户证照历史记录数据访问对象 */
+    private final StoreLicenseHistoryMapper licenseHistoryMapper;
+
     @ApiOperation("获取所有门店")
     @ApiOperationSupport(order = 1)
     @GetMapping("/getAll")
@@ -615,6 +622,14 @@ public class StoreInfoController {
                 storeInfo.setFoodLicenceStatus(storeInfoDto.getFoodLicenceStatus());
                 boolean flag = storeInfoService.updateById(storeInfo);
                 if (flag) {
+                    // 审核拒绝时修改提交记录
+                    LambdaUpdateWrapper<StoreLicenseHistory> wrapper = new LambdaUpdateWrapper<>();
+                    wrapper.eq(StoreLicenseHistory::getStoreId, storeInfo.getId());
+                    wrapper.eq(StoreLicenseHistory::getLicenseStatus, 2);
+                    wrapper.set(StoreLicenseHistory::getLicenseExecuteStatus, 3);
+                    wrapper.set(StoreLicenseHistory::getDeleteFlag, 1);
+                    licenseHistoryMapper.update(null, wrapper);
+
                     //待审核状态变为已审核
                     WebAudit webAudit = webAuditMapper.selectOne(new LambdaQueryWrapper<WebAudit>().eq(WebAudit::getStoreInfoId, storeInfo.getId()).eq(WebAudit::getDeleteFlag, 0).eq(WebAudit::getType, "7"));
                     if (webAudit != null) {

+ 35 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -30,7 +30,9 @@ import shop.alien.entity.store.excelVo.StoreInfoExpiredRecordsExcelVo;
 import shop.alien.entity.store.excelVo.util.ExcelExporter;
 import shop.alien.entity.store.excelVo.util.ExcelGenerator;
 import shop.alien.entity.store.vo.*;
+import shop.alien.entity.storePlatform.StoreLicenseHistory;
 import shop.alien.mapper.*;
+import shop.alien.mapper.storePlantform.StoreLicenseHistoryMapper;
 import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.config.GaoDeMapUtil;
 import shop.alien.store.config.WebSocketProcess;
@@ -135,6 +137,9 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
     private final LifeBlacklistMapper lifeBlacklistMapper;
 
+    /** 商户证照历史记录数据访问对象 */
+    private final StoreLicenseHistoryMapper licenseHistoryMapper;
+
     @Resource
     private StoreIncomeDetailsRecordService storeIncomeDetailsRecordService;
 
@@ -2196,6 +2201,19 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             storeImg.setImgType(24);
             storeImg.setImgDescription("经营许可证审核通过前图片");
             storeImgMapper.insert(storeImg);
+
+            licenseHistoryMapper.delete(new LambdaQueryWrapper<StoreLicenseHistory>()
+                    .eq(StoreLicenseHistory::getLicenseStatus,2)
+                    .eq(StoreLicenseHistory::getStoreId,storeImg.getStoreId()));
+            // 经营许可证历史表插入
+            StoreLicenseHistory licenseHistory = new StoreLicenseHistory();
+            licenseHistory.setStoreId(storeImg.getStoreId());
+            licenseHistory.setLicenseStatus(2);
+            licenseHistory.setLicenseExecuteStatus(2);
+            licenseHistory.setImgUrl(storeImg.getImgUrl());
+            licenseHistory.setDeleteFlag(0);
+            licenseHistoryMapper.insert(licenseHistory);
+
             //更新店铺
             StoreInfo storeInfo = new StoreInfo();
             storeInfo.setFoodLicenceStatus(2);
@@ -2284,6 +2302,23 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         LambdaUpdateWrapper<StoreImg> imgLambdaUpdateWrapper = new LambdaUpdateWrapper();
         imgLambdaUpdateWrapper.in(StoreImg::getId, imgList).set(StoreImg::getImgType, 25).set(StoreImg::getImgDescription,"经营许可证审核通过图片");
         int num = storeImgMapper.update(null, imgLambdaUpdateWrapper);
+
+        // 将原来的食品经营许可证历史表数据删除
+        licenseHistoryMapper.delete(new LambdaQueryWrapper<StoreLicenseHistory>()
+                .eq(StoreLicenseHistory::getStoreId,id)
+                .eq(StoreLicenseHistory::getLicenseStatus,2)
+                .eq(StoreLicenseHistory::getLicenseExecuteStatus,1)
+                .eq(StoreLicenseHistory::getDeleteFlag,0));
+
+        // 将新的食品经营许可证历史表数据变为审核通过
+        LambdaUpdateWrapper<StoreLicenseHistory> wrapper1 = new LambdaUpdateWrapper<>();
+        wrapper1.eq(StoreLicenseHistory::getStoreId, id);
+        wrapper1.eq(StoreLicenseHistory::getLicenseStatus, 2);
+        wrapper1.eq(StoreLicenseHistory::getLicenseExecuteStatus, 2);
+        wrapper1.eq(StoreLicenseHistory::getDeleteFlag, 0);
+        wrapper1.set(StoreLicenseHistory::getLicenseExecuteStatus, 1);
+        licenseHistoryMapper.update(null, wrapper1);
+
         return num;
     }