Przeglądaj źródła

Merge branch 'point_dev'

# Conflicts:
#	nightFragrance-admin/src/main/resources/application-dev.yml
#	nightFragrance-admin/src/main/resources/application-pro.yml
#	nightFragrance-admin/src/main/resources/application-test.yml
#	nightFragrance-framework/src/main/java/com/ylx/framework/web/service/WxTokenService.java
#	nightFragrance-massage/src/main/java/com/ylx/massage/service/TOrderService.java
#	nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TOrderServiceImpl.java
#	nightFragrance-massage/src/main/java/com/ylx/massage/task/massageTask.java
jinshihui 2 tygodni temu
rodzic
commit
b9699ef16d
100 zmienionych plików z 4543 dodań i 473 usunięć
  1. 2 0
      .gitignore
  2. 0 5
      README.md
  3. 2 1
      nightFragrance-admin/src/main/java/com/ylx/NightFragranceApplication.java
  4. 41 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/api/ApiProductSelectionController.java
  5. 31 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/api/ApiUserPointController.java
  6. 32 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/lottery/LotteryController.java
  7. 131 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/AfterSaleOrderController.java
  8. 36 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/AreaController.java
  9. 1 1
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/CouponController.java
  10. 79 8
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/CouponReceiveController.java
  11. 109 36
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/PayController.java
  12. 74 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductCategoryController.java
  13. 242 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductController.java
  14. 176 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductOrderController.java
  15. 63 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductSpecController.java
  16. 3 9
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TJsController.java
  17. 41 16
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TLbtController.java
  18. 1 2
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TOrderController.java
  19. 273 41
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WeChatController.java
  20. 8 8
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WeSqController.java
  21. 2 8
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WxController.java
  22. 146 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/point/PointActivityController.java
  23. 88 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/point/UserPointController.java
  24. 50 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/usercenter/UnifiedUserCenterController.java
  25. 1 3
      nightFragrance-admin/src/main/resources/application.yml
  26. 33 33
      nightFragrance-admin/src/main/resources/i18n/messages.properties
  27. 6 0
      nightFragrance-common/pom.xml
  28. 27 0
      nightFragrance-common/src/main/java/com/ylx/common/annotation/EnumTranslate.java
  29. 4 5
      nightFragrance-common/src/main/java/com/ylx/common/annotation/Log.java
  30. 119 0
      nightFragrance-common/src/main/java/com/ylx/common/aspect/EnumTranslateAspect.java
  31. 14 0
      nightFragrance-common/src/main/java/com/ylx/common/config/AmapConfig.java
  32. 1 1
      nightFragrance-common/src/main/java/com/ylx/common/config/WechatAccountConfig.java
  33. 1 1
      nightFragrance-common/src/main/java/com/ylx/common/config/WxPayConfig.java
  34. 4 0
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/BaseEntity.java
  35. 29 0
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/BindPhoneBody.java
  36. 31 0
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/PhoneLoginBody.java
  37. 12 1
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/WxLoginUser.java
  38. 16 0
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/AliyunApiProperties.java
  39. 13 0
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/SMSVerificationCode.java
  40. 66 0
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/SendSmsComponents.java
  41. 17 0
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/SendSmsEnum.java
  42. 3 6
      nightFragrance-common/src/main/java/com/ylx/common/core/page/TableSupport.java
  43. 1 3
      nightFragrance-common/src/main/java/com/ylx/common/enums/BusinessStatus.java
  44. 1 2
      nightFragrance-common/src/main/java/com/ylx/common/enums/BusinessType.java
  45. 3 6
      nightFragrance-common/src/main/java/com/ylx/common/utils/PageUtils.java
  46. 5 2
      nightFragrance-common/src/main/java/com/ylx/common/utils/SecurityUtils.java
  47. 7 0
      nightFragrance-framework/src/main/java/com/ylx/framework/aspectj/LogAspect.java
  48. 26 0
      nightFragrance-framework/src/main/java/com/ylx/framework/config/MyMetaObjectHandler.java
  49. 12 15
      nightFragrance-framework/src/main/java/com/ylx/framework/config/SecurityConfig.java
  50. 1 2
      nightFragrance-framework/src/main/java/com/ylx/framework/security/handle/AuthenticationEntryPointImpl.java
  51. 8 6
      nightFragrance-framework/src/main/java/com/ylx/framework/security/handle/LogoutSuccessHandlerImpl.java
  52. 24 45
      nightFragrance-framework/src/main/java/com/ylx/framework/web/service/PermissionService.java
  53. 2 1
      nightFragrance-framework/src/main/java/com/ylx/framework/web/service/TokenService.java
  54. 1 0
      nightFragrance-massage/pom.xml
  55. 56 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/LotteryCountLog.java
  56. 18 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/dto/IsLotteryDTO.java
  57. 24 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/dto/LotteryCountDTO.java
  58. 22 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/IsLotteryVO.java
  59. 36 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LocalActivityTableVO.java
  60. 21 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryActivityRulesProductVO.java
  61. 45 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryActivityVO.java
  62. 36 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryCountResponseVO.java
  63. 11 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryStatVO.java
  64. 20 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/mapper/LotteryCountLogMapper.java
  65. 17 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/service/LotteryCountLogService.java
  66. 14 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/service/LotteryCountService.java
  67. 87 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/service/impl/LotteryCountLogServiceImpl.java
  68. 177 0
      nightFragrance-massage/src/main/java/com/ylx/lottery/service/impl/LotteryCountServiceImpl.java
  69. 117 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/AfterSaleOrder.java
  70. 34 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/AfterSaleOrderFee.java
  71. 5 1
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/Area.java
  72. 18 6
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/Coupon.java
  73. 54 49
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/CouponReceive.java
  74. 177 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/Product.java
  75. 80 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductCategory.java
  76. 172 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductOrderInfo.java
  77. 119 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductOrderItem.java
  78. 108 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductOrderPayment.java
  79. 113 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductSku.java
  80. 60 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductSpec.java
  81. 69 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductSpecValue.java
  82. 4 1
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/RefundVoucher.java
  83. 2 2
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TAddress.java
  84. 46 93
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TConsumptionLog.java
  85. 30 8
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TJs.java
  86. 14 43
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TLbt.java
  87. 0 3
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TOrder.java
  88. 7 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TWxUser.java
  89. 125 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/UserCouponRelation.java
  90. 35 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderDTO.java
  91. 22 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderFeeBatchDTO.java
  92. 30 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderFeeDTO.java
  93. 23 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderUpdateDTO.java
  94. 17 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/CoordinateDTO.java
  95. 16 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/OptionDTO.java
  96. 145 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductCreateDTO.java
  97. 33 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderCancelRequest.java
  98. 114 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderCreateRequest.java
  99. 22 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderEditLogisticsNo.java
  100. 29 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderOperateDTO.java

+ 2 - 0
.gitignore

@@ -7,6 +7,8 @@
 
 target/
 !.mvn/wrapper/maven-wrapper.jar
+.claude/
+*.md
 
 ######################################################################
 # IDE

+ 0 - 5
README.md

@@ -1,5 +0,0 @@
-
-夜来香
-
-
-

+ 2 - 1
nightFragrance-admin/src/main/java/com/ylx/NightFragranceApplication.java

@@ -3,16 +3,17 @@ package com.ylx;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.scheduling.annotation.EnableAsync;
 
 /**
  * 启动程序
  *
  * @author ylx
  */
+@EnableAsync
 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
 public class NightFragranceApplication {
     public static void main(String[] args) {
-        // System.setProperty("spring.devtools.restart.enabled", "false");
         SpringApplication.run(NightFragranceApplication.class, args);
         System.out.println("启动成功");
     }

+ 41 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/api/ApiProductSelectionController.java

@@ -0,0 +1,41 @@
+package com.ylx.web.controller.api;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.core.domain.R;
+import com.ylx.massage.domain.dto.OptionDTO;
+import com.ylx.massage.domain.dto.ServiceOptionDTO;
+import com.ylx.massage.domain.vo.ProductOptionVO;
+import com.ylx.massage.domain.vo.ProductServiceOptionVO;
+import com.ylx.massage.service.ProductSelectionService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/products") // 假设的基础路径
+public class ApiProductSelectionController {
+
+    @Resource
+    private ProductSelectionService productSelectionService;
+
+    @ApiOperation("查询商品列表(支持服务与实物)")
+    @PostMapping(value = "/options")
+    public R<Page<ProductOptionVO>> optionsPage( @RequestBody OptionDTO dto) {
+        Page<ProductOptionVO> pageData = this.productSelectionService.optionsPage(dto);
+        return R.ok(pageData);
+    }
+
+    @ApiOperation("获取服务商品列表")
+    @PostMapping(value = "/options/service")
+    public R<Page<ProductServiceOptionVO>> queryServicePage(@RequestBody ServiceOptionDTO dto) {
+        Page<ProductServiceOptionVO> pageData = this.productSelectionService.serviceOptionsPage(dto);
+        return R.ok(pageData);
+    }
+
+}

+ 31 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/api/ApiUserPointController.java

@@ -0,0 +1,31 @@
+package com.ylx.web.controller.api;
+
+import com.ylx.common.core.domain.R;
+import com.ylx.point.domain.dto.ApiAddPointsDTO;
+import com.ylx.point.service.IPointAccountService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping("/api/user/point")
+@Api(tags = {"用户积分"})
+public class ApiUserPointController {
+
+    @Resource
+    private IPointAccountService pointAccountService;
+
+    @ApiOperation("本地生活抽奖,用户获得积分")
+    @PostMapping(value = "/add")
+    public R addPoints(@RequestBody @Validated ApiAddPointsDTO dto) {
+        this.pointAccountService.apiAddPoints(dto);
+        return R.ok();
+    }
+
+}

+ 32 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/lottery/LotteryController.java

@@ -0,0 +1,32 @@
+package com.ylx.web.controller.lottery;
+
+import com.ylx.common.core.domain.R;
+import com.ylx.lottery.domain.dto.IsLotteryDTO;
+import com.ylx.lottery.domain.vo.IsLotteryVO;
+import com.ylx.lottery.service.LotteryCountLogService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@RestController
+@RequestMapping("/lottery")
+public class LotteryController {
+
+    @Resource
+    private LotteryCountLogService lotteryCountLogService;
+
+    @ApiOperation("根据活动类型判断是否可以抽奖")
+    @PostMapping("/isLottery")
+    public R<IsLotteryVO> isLottery(@Validated @RequestBody IsLotteryDTO dto) {
+        IsLotteryVO vo = this.lotteryCountLogService.isLottery(dto);
+        return R.ok(vo);
+    }
+
+}

+ 131 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/AfterSaleOrderController.java

@@ -0,0 +1,131 @@
+package com.ylx.web.controller.massage;
+
+import com.ylx.common.annotation.Log;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.enums.BusinessType;
+import com.ylx.massage.domain.dto.AfterSaleOrderDTO;
+import com.ylx.massage.domain.dto.AfterSaleOrderFeeBatchDTO;
+import com.ylx.massage.domain.dto.AfterSaleOrderUpdateDTO;
+import com.ylx.massage.service.IAfterSaleOrderFeeService;
+import com.ylx.massage.service.IAfterSaleOrderService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@RestController
+@RequestMapping("/after/sale/order")
+public class AfterSaleOrderController {
+
+    @Resource
+    private IAfterSaleOrderFeeService afterSaleOrderFeeService;
+
+    @Resource
+    private IAfterSaleOrderService afterSaleOrderService;
+
+
+    @ApiOperation("批量添加售后费用")
+    @Log(title = "批量添加售后费用", businessType = BusinessType.INSERT)
+    @PostMapping("/fee/batch")
+    public R addFee(@Validated @RequestBody AfterSaleOrderFeeBatchDTO batchDTO) {
+        this.afterSaleOrderFeeService.batchSaveFees(batchDTO);
+        return R.ok();
+    }
+
+    /**
+     * 发起售后
+     * @param dto
+     * @return R
+     */
+    @ApiOperation("发起售后")
+    @Log(title = "发起售后", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R add(@Validated @RequestBody AfterSaleOrderDTO dto) {
+        try {
+            afterSaleOrderService.add(dto);
+            return R.ok();
+        } catch (Exception e) {
+            log.error("发起售后失败", e);
+            return R.fail("发起售后失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 取消退货/取消申请
+     * @param orderId
+     * @return R
+     */
+    @ApiOperation("取消退货/取消申请")
+    @Log(title = "取消退货/取消申请", businessType = BusinessType.UPDATE)
+    @PutMapping("/cancel/{orderId}")
+    public R cancel(@PathVariable Long orderId) {
+        try {
+            this.afterSaleOrderService.cancel(orderId);
+            return R.ok();
+        } catch (Exception e) {
+            log.error("取消退货/取消申请失败", e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    @ApiOperation("售后信息修改物流单号")
+    @Log(title = "售后信息修改物流单号", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R updateLogisticsNo(@Validated @RequestBody AfterSaleOrderUpdateDTO dto) {
+        this.afterSaleOrderService.edit(dto);
+        return R.ok();
+    }
+
+    @ApiOperation("公众号-修改售后物流单号")
+    @Log(title = "公众号修改售后物流单号", businessType = BusinessType.UPDATE)
+    @PutMapping("/wx")
+    public R wxUpdateLogisticsNo(@Validated @RequestBody AfterSaleOrderUpdateDTO dto) {
+        this.afterSaleOrderService.editByWechat(dto);
+        return R.ok();
+    }
+
+    @ApiOperation("商家同意退货申请")
+    @Log(title = "同意退货", businessType = BusinessType.UPDATE)
+    @PutMapping("/agree/return/{orderId}")
+    public R agreeReturn(@PathVariable Long orderId) {
+        this.afterSaleOrderService.agreeReturn(orderId);
+        return R.ok();
+    }
+
+    @ApiOperation("商家同意换货申请")
+    @Log(title = "同意换货", businessType = BusinessType.UPDATE)
+    @PutMapping("/agree/exchange/{orderId}")
+    public R agreeExchange(@PathVariable Long orderId) {
+        this.afterSaleOrderService.agreeExchange(orderId);
+        return R.ok();
+    }
+
+    /**
+     * 商家同意退款申请
+     * @param orderId
+     * @return R
+     */
+    @ApiOperation("商家同意退款申请")
+    @Log(title = "同意退款", businessType = BusinessType.UPDATE)
+    @PutMapping("/agree/refund/{orderId}")
+    public R agreeRefund(@PathVariable Long orderId) {
+        try {
+            this.afterSaleOrderService.agreeRefund(orderId);
+            return R.ok();
+        } catch (Exception e) {
+            log.error("同意退款申请失败", e);
+            return R.fail("同意退款申请失败", e.getMessage());
+        }
+    }
+
+    @ApiOperation("商家确认收货")
+    @Log(title = "确认收货", businessType = BusinessType.UPDATE)
+    @PutMapping("/confirm/receive/{orderId}")
+    public R confirmReceive(@PathVariable Long orderId) {
+        this.afterSaleOrderService.confirmReceive(orderId);
+        return R.ok();
+    }
+}

+ 36 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/AreaController.java

@@ -1,11 +1,15 @@
 package com.ylx.web.controller.massage;
 
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ylx.massage.domain.Area;
+import com.ylx.massage.domain.dto.CoordinateDTO;
+import com.ylx.massage.domain.vo.AreaTreeNode;
+import com.ylx.massage.domain.vo.CityInfoVo;
 import com.ylx.massage.service.AreaService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -14,6 +18,7 @@ import com.ylx.common.core.domain.R;
 import javax.annotation.Resource;
 import java.io.Serializable;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 区域数据(Area)表控制层
@@ -95,5 +100,36 @@ public class AreaController {
     public R delete(@RequestParam("idList") List<Long> idList) {
         return R.ok(this.areaService.removeByIds(idList));
     }
+
+    /**
+     * 根据城市名称查询编码
+     * @param name
+     * @return R
+     */
+    @GetMapping("/code")
+    @ApiOperation("name获取code")
+    public R getCodeByName(@RequestParam String name) {
+        LambdaQueryWrapper<Area> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(Area::getName, name);
+        List<Area> list = this.areaService.list(queryWrapper);
+        if (CollectionUtil.isEmpty(list)) {
+            return R.fail("未找到此数据");
+        }
+        return R.ok(CollectionUtil.getFirst(list).getCode());
+    }
+
+    @GetMapping("/tree")
+    @ApiOperation("获取全量区域树形结构")
+    public R<List<AreaTreeNode>> getAreaTree() {
+        List<AreaTreeNode> tree = this.areaService.getAreaTree();
+        return R.ok(tree);
+    }
+
+    @PostMapping("/city")
+    @ApiOperation("根据经纬度获取城市信息")
+    public R<CityInfoVo> getCityInfoByCoordinates(@RequestBody CoordinateDTO dto) {
+        CityInfoVo cityInfoVo = this.areaService.getCityInfoByCoordinates(dto);
+        return R.ok(cityInfoVo);
+    }
 }
 

+ 1 - 1
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/CouponController.java

@@ -37,7 +37,7 @@ public class CouponController extends BaseController {
     private CouponService couponService;
 
     /**
-     * 分页查询所有数据
+     * 分页查询所有优惠券数据(PC端)
      *
      * @param page   分页对象
      * @param coupon 查询实体

+ 79 - 8
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/CouponReceiveController.java

@@ -1,6 +1,6 @@
 package com.ylx.web.controller.massage;
 
-
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ylx.common.annotation.Log;
@@ -8,12 +8,16 @@ import com.ylx.common.enums.BusinessType;
 import com.ylx.massage.domain.Coupon;
 import com.ylx.massage.domain.CouponReceive;
 import com.ylx.common.core.domain.R;
+import com.ylx.massage.domain.vo.ClaimCouponRequestVO;
 import com.ylx.massage.domain.vo.CouponReceiveVo;
 import com.ylx.massage.domain.vo.CouponReceivesVO;
+import com.ylx.massage.domain.vo.ShareVolutionDetailVO;
 import com.ylx.massage.service.CouponReceiveService;
+import com.ylx.massage.service.ShareVolutionService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
@@ -28,7 +32,7 @@ import java.util.List;
 @RestController
 @RequestMapping("couponReceive")
 @Slf4j
-@Api(tags = {"优惠劵认领"})
+@Api(tags = { "优惠劵认领" })
 public class CouponReceiveController {
     /**
      * 服务对象
@@ -36,6 +40,9 @@ public class CouponReceiveController {
     @Resource
     private CouponReceiveService couponReceiveService;
 
+    @Resource
+    private ShareVolutionService shareVolutionService;
+
     /**
      * 分页查询所有数据
      *
@@ -62,29 +69,75 @@ public class CouponReceiveController {
     }
 
     /**
-     * 新增数据
+     * 领取优惠卷
      *
      * @param couponReceive 实体对象
-     * @return 新增结果
+     * @return R 新增结果
      */
     @PostMapping("submit")
     @ApiOperation("领取优惠卷")
     public R insert(@RequestBody CouponReceive couponReceive) {
-        return R.ok(this.couponReceiveService.submit(couponReceive));
+        try {
+            return R.ok(this.couponReceiveService.submit(couponReceive));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
 
+    /**
+     * 首页领取优惠卷(批量领取优惠券)
+     *
+     * @param couponReceive
+     * @return R
+     */
     @PostMapping("submits")
     @ApiOperation("首页领取优惠卷")
     @Log(title = "首页领取优惠卷", businessType = BusinessType.INSERT)
     public R insert(@RequestBody CouponReceivesVO couponReceive) {
-        return R.ok(this.couponReceiveService.submits(couponReceive));
+        try {
+            return R.ok(this.couponReceiveService.submits(couponReceive));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 用户领取劵中心的优惠劵
+     *
+     * @param claimCouponRequestVO
+     * @return R<?>
+     */
+    @ApiOperation(value = "用户领取劵中心的优惠劵", notes = "用户领取劵中心的优惠劵")
+    @PostMapping(value = "/receiveCoupon")
+    public R<?> receiveCoupon(@Validated @RequestBody ClaimCouponRequestVO claimCouponRequestVO) {
+        try {
+            log.info("用户领取劵中心的优惠劵请求参数:{}", JSON.toJSONString(claimCouponRequestVO));
+            return couponReceiveService.receiveVolution(claimCouponRequestVO);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.fail(e.getMessage());
+        }
     }
 
+    /**
+     * 查询我的优惠卷
+     *
+     * @param couponReceive
+     * @return R<List<CouponReceiveVo>>
+     */
     @PostMapping("myCoupon")
     @ApiOperation("我的优惠卷")
     public R<List<CouponReceiveVo>> myCoupon(@RequestBody CouponReceive couponReceive) {
-        return R.ok(this.couponReceiveService.getByOpenId(couponReceive.getOpenid()));
+        try {
+            return R.ok(this.couponReceiveService.getByOpenId(couponReceive.getReceiveOpenId()));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
+
     /**
      * 删除数据
      *
@@ -102,5 +155,23 @@ public class CouponReceiveController {
     public R<List<Coupon>> couponWindows(@RequestBody CouponReceive couponReceive) {
         return R.ok(this.couponReceiveService.couponWindows(couponReceive));
     }
-}
 
+    /**
+     * 通过优惠券ID查询优惠券详情(调用本地生活的接口)
+     *
+     * @param couponId
+     * @return R<ShareVolutionDetailVO>
+     */
+    @GetMapping("getShareVolutionDetail")
+    @ApiOperation("通过优惠券ID查询优惠券详情")
+    public R<ShareVolutionDetailVO> getShareVolutionDetail(@RequestParam("couponId") String couponId) {
+        try {
+            log.info("通过优惠券ID查询优惠券详情, couponId: {}", couponId);
+            return shareVolutionService.getVolutionDetailByCouponId(couponId);
+        } catch (Exception e) {
+            log.error("通过优惠券ID查询优惠券详情异常, couponId: {}", couponId, e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+}

+ 109 - 36
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/PayController.java

@@ -28,9 +28,17 @@ import com.ylx.common.config.WxPayConfig;
 import com.ylx.common.core.domain.R;
 import com.ylx.massage.domain.TRecharge;
 import com.ylx.massage.enums.BillTypeEnum;
+import com.ylx.massage.enums.PayTypeEnum;
+import com.ylx.massage.domain.ProductOrderInfo;
+import com.ylx.massage.domain.TConsumptionLog;
+import com.ylx.massage.domain.TWxUser;
+import com.ylx.massage.enums.BillTypeEnum;
+import com.ylx.massage.enums.ProductOrderStatusEnum;
+import com.ylx.massage.service.IProductOrderInfoService;
 import com.ylx.massage.service.RefundVoucherService;
 import com.ylx.massage.service.TOrderService;
 import com.ylx.massage.service.TRechargeService;
+import com.ylx.massage.service.TWxUserService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
@@ -72,10 +80,17 @@ public class PayController {
     @Resource
     private RefundVoucherService refundVoucherService;
 
-//    @Resource
+    @Resource
+    private IProductOrderInfoService productOrderInfoService;
+
+    @Resource
+    private TWxUserService wxUserService;
+
+    //    @Resource
 //    private JsapiServiceExtension service;
     String serialNo;
     String platSerialNo;
+
     /**
      * 小程序微信支付的第一步,统一下单
      */
@@ -88,6 +103,7 @@ public class PayController {
 
     /**
      * 支付
+     *
      * @param setOutTradeNo
      * @param amount
      * @param openId
@@ -195,6 +211,10 @@ public class PayController {
         return R.ok(JSONUtil.toJsonStr(response));
     }
 
+    /**
+     * 获取商户API证书序列号
+     * @return String 证书序列号
+     */
     private String getSerialNumber() {
         if (StrUtil.isEmpty(serialNo)) {
             // 获取证书序列号
@@ -220,6 +240,12 @@ public class PayController {
         return serialNo;
     }
 
+    /**
+     * 微信支付回调接口
+     *
+     * @param request
+     * @param response
+     */
     @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
     @ResponseBody
     @ApiOperation("微信支付回调接口")
@@ -249,8 +275,14 @@ public class PayController {
                 // 处理业务逻辑
                 JSONObject jsonObject = new JSONObject(plainText);
                 if (jsonObject.get("attach").equals(BillTypeEnum.WX_PAY.getCode().toString())) {
-                    // 订单支付成功
+                    // 服务订单支付成功
                     orderService.payNotifyOrder(jsonObject.get("out_trade_no").toString());
+                } else if (jsonObject.get("attach").equals(PayTypeEnum.WX_PAY.getCode().toString())) {
+                    // 商品订单支付成功
+                    String productOrderNo = jsonObject.get("out_trade_no").toString();
+                    log.info("商品订单支付回调开始处理,订单号:{}", productOrderNo);
+                    productOrderInfoService.handleWxPayCallback(productOrderNo);
+                    log.info("商品订单支付回调处理完成,订单号:{}", productOrderNo);
                 } else {
                     TRecharge outTradeNo = rechargeService.increaseAmount(jsonObject.get("out_trade_no").toString());
                 }
@@ -268,7 +300,6 @@ public class PayController {
     }
 
 
-
     @RequestMapping(value = "/test", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
     @ResponseBody
     @ApiOperation("测试")
@@ -361,6 +392,11 @@ public class PayController {
         }
     }
 
+    /**
+     * 微信批量提现
+     * @param openId
+     * @return String
+     */
     @RequestMapping("/batchTransfer")
     @ApiOperation("微信批量提现")
     @ResponseBody
@@ -407,45 +443,29 @@ public class PayController {
 
     /**
      * 退款
-     * @param outRefundNo 退款订单号
-     * @param amount 退款金额
+     *
+     * @param outRefundNo   退款订单号
+     * @param amount        退款金额
      * @param transactionId 微信支付订单号
-     * @param outTradeNo 商户订单号
-     * @return String
+     * @param outTradeNo    商户订单号
+     * @return String 退款结果
      */
     @RequestMapping("/refund")
     @ResponseBody
-    public String refund(@RequestParam(required = false) String outRefundNo, @RequestParam(required = false) BigDecimal amount,@RequestParam(required = false) String transactionId, @RequestParam(required = false) String outTradeNo) {
-        return rechargeService.refund(outRefundNo,transactionId,outTradeNo,amount);
+    public String refund(@RequestParam(required = false) String outRefundNo, @RequestParam(required = false) BigDecimal amount, @RequestParam(required = false) String transactionId, @RequestParam(required = false) String outTradeNo) {
+        try {
+            return rechargeService.refund(outRefundNo, transactionId, outTradeNo, amount);
+        } catch (Exception e) {
+            log.error("退款异常", e);
+            throw new RuntimeException(e);
+        }
     }
 
-//    /**
-//     * 退款通知
-//     */
-//    @RequestMapping(value = "/refundNotify", method = {RequestMethod.POST, RequestMethod.GET})
-//    @ResponseBody
-//    @ApiOperation("微信退款回调接口")
-//    public String refundNotify(HttpServletRequest request) {
-//        String xmlMsg = HttpKit.readData(request);
-//        log.info("退款通知=" + xmlMsg);
-//        Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
-//        log.info("退款通知parms:{}",params);
-//        String returnCode = params.get("event_type");
-//        // 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
-//        if (returnCode.equals("REFUND.SUCCESS")) {
-//            String reqInfo = params.get("resource");
-//            String decryptData = WxPayKit.decryptData(reqInfo, WxPayApiConfigKit.getWxPayApiConfig().getPartnerKey());
-//            log.info("退款通知解密后的数据=" + decryptData);
-////            refundVoucherService.
-//            // 更新订单信息
-//            // 发送通知等
-//            Map<String, String> xml = new HashMap<String, String>(2);
-//            xml.put("return_code", "SUCCESS");
-//            xml.put("return_msg", "OK");
-//            return WxPayKit.toXml(xml);
-//        }
-//        return null;
-//    }
+    /**
+     * 微信退款回调接口
+     * @param request
+     * @param response
+     */
     @ApiOperation("微信退款回调接口")
     @RequestMapping(value = "/refundNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
     public void refundWechatCallback(HttpServletRequest request, HttpServletResponse response) {
@@ -490,5 +510,58 @@ public class PayController {
     }
 
 
+    /**
+     * 根据商户订单号查询微信支付订单
+     *
+     * @param outTradeNo 商户订单号
+     * @return R 订单详情
+     */
+    @GetMapping("/query/order/{outTradeNo}")
+    @ApiOperation("根据商户订单号查询微信支付订单")
+    public R queryOrderByOutTradeNo(@PathVariable("outTradeNo") String outTradeNo) {
+        try {
+            log.info("查询微信支付订单,商户订单号:{}", outTradeNo);
+
+            // V3 API:根据商户订单号查询订单接口路径
+            // GET /v3/pay/transactions/out-trade-no/{out_trade_no}
+            String queryUrl = String.format("/v3/pay/transactions/out-trade-no/%s", outTradeNo);
+            log.info("查询订单URL:{}", queryUrl);
+            Map<String, String> queryParams = new HashMap<>();
+            queryParams.put("mchid", wxPayProperties.getMchId());
+            // 调用微信支付V3接口查询订单
+            IJPayHttpResponse response = WxPayApi.v3(
+                    RequestMethodEnum.GET,
+                    WxDomainEnum.CHINA.toString(),
+                    queryUrl,
+                    wxPayProperties.getMchId(),
+                    getSerialNumber(),
+                    null,
+                    wxPayProperties.getCertKeyPath(),
+                    queryParams
+            );
+            log.info("查询订单响应状态:{},响应体:{}", response.getStatus(), response.getBody());
+            // 处理响应
+            if (response.getStatus() == SUCCESS) {
+                // 验证响应签名
+                boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
+                log.info("响应签名验证结果:{}", verifySignature);
+
+                if (verifySignature) {
+                    log.info("商户订单号 {} 查询成功,订单信息:{}", outTradeNo, response.getBody());
+                    JSONObject result = JSONUtil.parseObj(response.getBody());
+                    return R.ok(result);
+                } else {
+                    log.error("商户订单号 {} 查询响应签名验证失败", outTradeNo);
+                    return R.fail("查询订单响应签名验证失败");
+                }
+            } else {
+                log.error("商户订单号 {} 查询失败,状态码:{},响应:{}", outTradeNo, response.getStatus(), response.getBody());
+                return R.fail("查询订单失败:" + response.getBody());
+            }
+        } catch (Exception e) {
+            log.error("查询微信支付订单异常,商户订单号:{},错误:{}", outTradeNo, e.getMessage(), e);
+            return R.fail("查询订单异常:" + e.getMessage());
+        }
+    }
 
 }

+ 74 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductCategoryController.java

@@ -0,0 +1,74 @@
+package com.ylx.web.controller.massage;
+
+import com.ylx.common.annotation.Log;
+import com.ylx.common.core.controller.BaseController;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.enums.BusinessType;
+import com.ylx.massage.domain.ProductCategory;
+import com.ylx.massage.domain.vo.CategoryTreeVO;
+import com.ylx.massage.service.ProductCategoryService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Slf4j
+@Api(tags = {"商品分类管理"})
+@RestController
+@RequestMapping("product")
+public class ProductCategoryController extends BaseController {
+
+    @Resource
+    private ProductCategoryService productCategoryService;
+
+    /**
+     * 新增商品分类
+     *
+     * @param category 分类实体
+     * @return R 新增结果
+     */
+    @PostMapping("category/create")
+    @ApiOperation("新增商品分类")
+    public R createCategory(@RequestBody ProductCategory category) {
+        return R.ok(productCategoryService.save(category));
+    }
+
+    /**
+     * 修改商品分类
+     *
+     * @param category 分类实体
+     * @return 修改结果
+     */
+    @PostMapping("category/update")
+    @Log(title = "商品分类修改", businessType = BusinessType.UPDATE)
+    @ApiOperation("修改商品分类")
+    public R updateCategory(@RequestBody ProductCategory category) {
+        return R.ok(productCategoryService.updateById(category));
+    }
+
+    /**
+     * 删除商品分类
+     *
+     * @param id 分类ID
+     * @return 删除结果
+     */
+    @PostMapping("category/delete")
+    @ApiOperation("删除商品分类")
+    public R deleteCategory(@RequestParam("id") Long id) {
+        return R.ok(productCategoryService.removeById(id));
+    }
+
+    /**
+     * 查询商品分类树形结构
+     *
+     * @return R<List<CategoryTreeVO>> 分类树形列表
+     */
+    @GetMapping("category/list")
+    @ApiOperation("查询商品分类树形结构")
+    public R<List<CategoryTreeVO>> categoryList() {
+        return R.ok(productCategoryService.getCategoryTree());
+    }
+}

+ 242 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductController.java

@@ -0,0 +1,242 @@
+package com.ylx.web.controller.massage;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.annotation.Log;
+import com.ylx.common.core.controller.BaseController;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.enums.BusinessType;
+import com.ylx.common.exception.ServiceException;
+import com.ylx.common.utils.StringUtils;
+import com.ylx.massage.domain.Product;
+import com.ylx.massage.domain.ProductCategory;
+import com.ylx.massage.domain.dto.ProductCreateDTO;
+import com.ylx.massage.domain.dto.ProductSpecSetupDTO;
+import com.ylx.massage.domain.dto.ProductUpdateDTO;
+import com.ylx.massage.domain.vo.CategoryTreeVO;
+import com.ylx.massage.domain.vo.H5ProductVo;
+import com.ylx.massage.domain.vo.ProductDetailsVo;
+import com.ylx.massage.domain.vo.SpecComboVO;
+import com.ylx.massage.service.ProductCategoryService;
+import com.ylx.massage.service.ProductService;
+import com.ylx.massage.service.ProductSkuService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 商品管理(Product)表控制层
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Slf4j
+@Api(tags = {"商品管理"})
+@RestController
+@RequestMapping("product")
+public class ProductController extends BaseController {
+
+    @Resource
+    private ProductService productService;
+
+    @Resource
+    private ProductCategoryService productCategoryService;
+
+    @Resource
+    private ProductSkuService productSkuService;
+
+    /**
+     * 分页查询商品列表(PC端)
+     *
+     * @param page    分页对象
+     * @param product 商品实体
+     * @return R<Page < Product>> 所有数据
+     */
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    @ApiOperation("PC查询商品管理列表")
+    public R<Page<Product>> selectAll(Page<Product> page, Product product) {
+        try {
+            LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(product.getCategoryId() != null, Product::getCategoryId, product.getCategoryId())
+                    .like(StringUtils.isNotBlank(product.getName()), Product::getName, product.getName())
+                    .like(StringUtils.isNotBlank(product.getProductNo()), Product::getProductNo, product.getProductNo())
+                    .eq(product.getStatus() != null, Product::getStatus, product.getStatus())
+                    .orderByDesc(Product::getCreateTime);
+            Page<Product> page1 = this.productService.page(page, queryWrapper);
+            //根据分类ID查询分类的名称
+            List<Product> records = page1.getRecords();
+            for (Product record : records) {
+                ProductCategory productCategory = this.productCategoryService.getById(record.getCategoryId());
+                record.setCategoryName(productCategory.getName());
+            }
+            return R.ok(page1);
+        } catch (Exception e) {
+            log.error("商品查询失败", e);
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    /**
+     * 分页查询商品列表(H5端)
+     *
+     * @param page      分页对象
+     * @param product   商品实体
+     * @param name      商品名称
+     * @param sortField 排序字段(price:价格, sales:销量, comprehensive:综合排序)
+     *                   综合排序规则:
+     *                   1. 销量热度(50%):近30天销量/平台总销量,min(1, ratio)
+     *                   2. 新品系数(30%):上架前7天=1.0,之后递减max(0, 1-(days-7)/23)
+     *                   3. 积分性价比(20%):积分值/商品价值
+     * @param sortOrder 排序方式(asc:升序, desc:降序)
+     * @return R<Page < H5ProductVo>> 所有数据
+     */
+    @RequestMapping(value = "/h5List", method = RequestMethod.GET)
+    @ApiOperation("H5查询商品管理列表")
+    public R<Page<H5ProductVo>> selectH5All(Page<H5ProductVo> page, Product product, @RequestParam(required = false) String name, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder) {
+        try {
+            Page<H5ProductVo> pageResult = productService.selectH5Page(page, product, name, sortField, sortOrder);
+            //根据分类ID查询分类的名称
+            for (H5ProductVo record : pageResult.getRecords()) {
+                ProductCategory productCategory = this.productCategoryService.getById(record.getCategoryId());
+                if (productCategory != null) {
+                    // 设置分类名称
+                    record.setCategoryName(productCategory.getName());
+                }
+                //根据商品ID查询库存的最低价格
+                record.setOriginalPrice(productSkuService.findMinPriceByProductId(record.getId()));
+            }
+            return R.ok(pageResult);
+        } catch (Exception e) {
+            log.error("商品查询失败", e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 通过主键查询商品详情
+     *
+     * @param id 主键
+     * @return R
+     */
+    @GetMapping("getById")
+    @ApiOperation("通过主键查询商品详情")
+    public R selectOne(@RequestParam Long id) {
+        try {
+            Product productServiceById = this.productService.getById(id);
+            if(productServiceById == null){
+                throw new ServiceException("商品不存在");
+            }
+            // 根据分类ID查询分类的名称
+            ProductCategory productCategory = this.productCategoryService.getById(productServiceById.getCategoryId());
+            productServiceById.setCategoryName(productCategory.getName());
+            //根据商品编号查询商品的规格
+            List<SpecComboVO> specComboVOList = this.productService.getSpecCombinations(productServiceById.getProductNo());
+            ProductDetailsVo productDetailsVo = new ProductDetailsVo();
+            BeanUtil.copyProperties(productServiceById, productDetailsVo);
+            productDetailsVo.setSpecComboVOList(specComboVOList);
+            return R.ok(productDetailsVo);
+        } catch (Exception e) {
+            log.error("商品查询失败", e);
+            return R.fail("商品查询失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 新增商品
+     *
+     * @param dto 新增商品请求DTO
+     * @return R 新增结果
+     */
+    @PostMapping("create")
+    @ApiOperation("新增商品")
+    public R create(@Valid @RequestBody ProductCreateDTO dto) {
+        try {
+            Long productId = productService.createProduct(dto);
+            return R.ok("商品ID:" + productId, "商品创建成功");
+        } catch (Exception e) {
+            log.error("商品创建失败", e);
+            return R.fail("商品创建失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 修改商品
+     *
+     * @param dto 商品实体对象
+     * @return R 修改结果
+     */
+    @PostMapping("update")
+    @ApiOperation("修改商品")
+    public R update(@Valid @RequestBody ProductUpdateDTO dto) {
+        try {
+            productService.updateProduct(dto);
+            return R.ok("商品修改成功");
+        } catch (Exception e) {
+            log.error("商品修改失败", e);
+            return R.fail("商品修改失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除商品
+     *
+     * @param idList 主键结合
+     * @return R 删除结果
+     */
+    @PostMapping("delete")
+    @ApiOperation("删除商品")
+    public R delete(@RequestBody List<Long> idList) {
+        return R.ok(this.productService.removeByIds(idList));
+    }
+
+    /**
+     * 上下架商品
+     *
+     * @param id     商品ID
+     * @param status 状态:1上架 0下架
+     * @return R 操作结果
+     */
+    @PostMapping("updateStatus")
+    @ApiOperation("上下架商品")
+    public R updateStatus(@RequestBody Product productDto) {
+        try {
+            log.info("商品上下架请求:{}", JSON.toJSONString(productDto));
+            Product product = new Product();
+            product.setId(productDto.getId());
+            product.setStatus(productDto.getStatus());
+            return R.ok(this.productService.updateById(product));
+        } catch (Exception e) {
+            log.error("商品上下架失败", e);
+            return R.fail("商品上下架失败:" + e.getMessage());
+        }
+    }
+
+
+    /**
+     * 生成商品编号
+     * 规则:分类编码 + 当前日期(yyyyMMdd) + 序列号(3位)
+     * 示例:AD20260327001
+     *
+     * @param categoryId 分类ID
+     * @return R<String> 生成的商品编号
+     */
+    @GetMapping("generateProductNo")
+    @ApiOperation("生成商品编号")
+    public R<String> generateProductNo(@RequestParam("categoryId") Long categoryId) {
+        try {
+            String productNo = productService.generateProductNo(categoryId);
+            return R.ok(productNo, "商品编号生成成功");
+        } catch (Exception e) {
+            log.error("商品编号生成失败", e);
+            return R.fail("商品编号生成失败:" + e.getMessage());
+        }
+    }
+}

+ 176 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductOrderController.java

@@ -0,0 +1,176 @@
+package com.ylx.web.controller.massage;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.core.controller.BaseController;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.core.domain.model.WxLoginUser;
+import com.ylx.common.utils.SecurityUtils;
+import com.ylx.massage.domain.ProductOrderInfo;
+import com.ylx.massage.domain.dto.*;
+import com.ylx.massage.domain.vo.*;
+import com.ylx.massage.service.IProductOrderInfoService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 商品订单控制器
+ */
+@Slf4j
+@RestController
+@RequestMapping("/product/order")
+public class ProductOrderController extends BaseController {
+
+    @Autowired
+    private IProductOrderInfoService productOrderInfoService;
+
+    /**
+     * 创建商品订单
+     *
+     * @param request 订单创建请求
+     * @return R<String> 订单编号
+     */
+    @PostMapping("/create")
+    public R<String> createProductOrder(@RequestBody ProductOrderCreateRequest request) {
+        try {
+            log.info("创建商品订单,请求参数:{}", JSON.toJSONString(request));
+            String orderNo = productOrderInfoService.createProductOrder(request);
+            log.info("商品订单创建成功,订单编号:{}", orderNo);
+            return R.ok(orderNo);
+        } catch (Exception e) {
+            log.error("商品订单创建失败:{}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 支付订单
+     *
+     * @param productOrderPayRequest 订单支付请求
+     * @return R 订单支付结果
+     */
+    @PostMapping("/pay")
+    public R payProductOrder(@RequestBody ProductOrderPayRequest productOrderPayRequest) {
+        try {
+            log.info("支付订单,请求参数的值:{}", JSON.toJSONString(productOrderPayRequest));
+            return productOrderInfoService.payOrder(productOrderPayRequest);
+        } catch (Exception e) {
+            log.error("订单支付失败:{}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 查询我的订单列表(分页)
+     *
+     * @param page   分页参数
+     * @param openId 用户openId
+     * @return R<Page < ProductOrderListVo>> 订单分页列表
+     */
+    @GetMapping("/list")
+    public R<Page<ProductOrderListVo>> getProductOrderList(Page<ProductOrderListVo> page, @RequestParam("openId") String openId) {
+        try {
+            log.info("查询用户订单列表,openId:{}", openId);
+            Page<ProductOrderListVo> result = productOrderInfoService.getProductOrderList(page, openId);
+            return R.ok(result);
+        } catch (Exception e) {
+            log.error("查询用户订单列表失败:{}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 查询商品订单详情
+     *
+     * @param orderNo 订单编号
+     * @return R<ProductOrderDetailResponse> 订单详情
+     */
+    @GetMapping("/detail")
+    public R<ProductOrderDetailResponse> getProductOrderDetail(@RequestParam("orderNo") String orderNo) {
+        try {
+            // 当前登录用户信息
+            WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser();
+            String openId = wxLoginUser.getCOpenid();
+            log.info("查询商品订单详情,openId:{},订单编号:{}", openId, orderNo);
+            ProductOrderDetailResponse detail = productOrderInfoService.getProductOrderDetail(orderNo, openId);
+            return R.ok(detail);
+        } catch (Exception e) {
+            log.error("查询商品订单详情失败:{}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 取消订单
+     *
+     * @param request 取消订单请求
+     * @return R 取消结果
+     */
+    @PostMapping("/cancel")
+    public R<?> cancelProductOrder(@RequestBody ProductOrderCancelRequest request) {
+        try {
+            log.info("取消商品订单,请求参数:{}", JSON.toJSONString(request));
+            return productOrderInfoService.cancelOrder(request);
+        } catch (Exception e) {
+            log.error("取消商品订单失败:{}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 订单确认收货
+     * @param dto
+     * @return R
+     */
+    @ApiOperation("订单确认收货接口")
+    @PutMapping("/confirm/receipt")
+    public R confirmReceipt(@Validated @RequestBody ProductOrderOperateDTO dto) {
+        this.productOrderInfoService.confirmReceipt(dto);
+        return R.ok();
+    }
+
+    @PreAuthorize("@ss.hasPermi('product:order:query')")
+    @PostMapping("/admin/page")
+    @ApiOperation("积分订单管理分页接口")
+    public R<Page<ProductOrderPageItemVo>> adminPage(Page<ProductOrderInfo> page, @RequestBody ProductOrderPageDTO dto) {
+        Page<ProductOrderPageItemVo> pageData = this.productOrderInfoService.adminPage(page, dto);
+        return R.ok(pageData);
+    }
+
+    @PreAuthorize("@ss.hasPermi('product:order:query')")
+    @GetMapping("/admin/page/stats")
+    @ApiOperation("积分订单管理统计接口")
+    public R<ProductOrderCountVo> adminPageStats() {
+        ProductOrderCountVo vo = this.productOrderInfoService.adminPageStats();
+        return R.ok(vo);
+    }
+
+    @PreAuthorize("@ss.hasPermi('product:order:query')")
+    @GetMapping("/admin/detail/{orderId}")
+    @ApiOperation("积分订单管理详情接口")
+    public R<ProductOrderDetailAdminVo> getOrderDetailForAdmin(@PathVariable Long orderId) {
+        ProductOrderDetailAdminVo detailVO = productOrderInfoService.getOrderDetailForAdmin(orderId);
+        return R.ok(detailVO);
+    }
+
+    @PreAuthorize("@ss.hasPermi('product:order:edit')")
+    @PutMapping("/admin/edit/logisticsNo")
+    @ApiOperation("积分订单管理详情修改物流单号接口")
+    public R editLogisticsNo(@Validated @RequestBody ProductOrderEditLogisticsNo dto) {
+        this.productOrderInfoService.editLogisticsNo(dto);
+        return R.ok();
+    }
+
+    @PreAuthorize("@ss.hasPermi('product:order:edit')")
+    @PutMapping("/admin/edit/consignees/info")
+    @ApiOperation("积分订单管理详情修改收货人信息接口")
+    public R editConsigneesInfo(@Validated @RequestBody ProductOrderOperateDTO dto) {
+        this.productOrderInfoService.editConsigneesInfo(dto);
+        return R.ok();
+    }
+
+}

+ 63 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ProductSpecController.java

@@ -0,0 +1,63 @@
+package com.ylx.web.controller.massage;
+
+import com.alibaba.fastjson.JSON;
+import com.ylx.common.core.controller.BaseController;
+import com.ylx.common.core.domain.R;
+import com.ylx.massage.domain.dto.ProductSpecSetupDTO;
+import com.ylx.massage.domain.vo.SpecComboVO;
+import com.ylx.massage.service.ProductService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+
+@Slf4j
+@Api(tags = {"商品规格管理"})
+@RestController
+@RequestMapping("product")
+public class ProductSpecController extends BaseController {
+
+    @Resource
+    private ProductService productService;
+
+
+    /**
+     * 新增(更新)商品规格
+     *
+     * @param dto 规格设置请求DTO
+     * @return R 操作结果
+     */
+    @PostMapping("spec/setup")
+    @ApiOperation("新增商品规格")
+    public R setupSpec(@Valid @RequestBody ProductSpecSetupDTO dto) {
+        try {
+            log.info("新增商品规格参数的值:{}", JSON.toJSONString(dto));
+            productService.setupSpec(dto);
+            return R.ok("新增规格成功");
+        } catch (Exception e) {
+            log.error("新增规格失败", e);
+            return R.fail("新增规格失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取商品规格组合(笛卡尔积)
+     *
+     * @param productId 商品ID
+     * @return R<List < SpecComboVO>> 规格组合列表
+     */
+    @GetMapping("spec/combinations")
+    @ApiOperation("获取商品规格组合")
+    public R<List<SpecComboVO>> getSpecCombinations(@RequestParam String productNo) {
+        try {
+            return R.ok(productService.getSpecCombinations(productNo));
+        } catch (Exception e) {
+            log.error("获取商品规格组合失败", e);
+            return R.fail("获取商品规格组合失败:" + e.getMessage());
+        }
+    }
+}

+ 3 - 9
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TJsController.java

@@ -30,13 +30,8 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.*;
-
-
 import javax.annotation.Resource;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.util.*;
-import java.util.stream.Collectors;
 
 /**
  * 技师Controller
@@ -214,17 +209,16 @@ public class TJsController extends BaseController {
     /**
      * 微信查询技师列表(用户端)
      *
-     * @param page
+     * @param page 分页参数
      * @param js
      * @return R 技师列表
      */
     @RequestMapping(value = "wx/select", method = RequestMethod.GET)
-    @Log(title = "微信查询技师列表", businessType = BusinessType.OTHER)
     @ApiOperation("微信查询技师列表")
     public R wxSelect(Page<TJs> page, TJsVo js) {
         try {
             log.info("微信查询技师列表,请求参数:{}", JSON.toJSONString(js));
-            if (js.getLatitude() != null && js.getLongitude() != null) {
+            /*if (js.getLatitude() != null && js.getLongitude() != null) {
                 // 检查经纬度是否在合理范围内
                 Location location = new Location();
                 location.setLatitude(js.getLatitude().doubleValue());
@@ -258,7 +252,7 @@ public class TJsController extends BaseController {
                     //log.info("page============>:{}", JSONUtil.toJsonStr(page));
                     return R.ok(page);
                 }
-            }
+            }*/
             return R.ok(jsService.getAll(page, js));
         } catch (Exception e) {
             // 异常处理

+ 41 - 16
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TLbtController.java

@@ -1,5 +1,6 @@
 package com.ylx.web.controller.massage;
 
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -36,28 +37,34 @@ public class TLbtController {
     /**
      * 获取轮播图
      *
-     * @return
+     * @return R<List<TLbt>>
      */
     @ApiOperation("获取轮播图")
     @RequestMapping(value = "/getAll", method = RequestMethod.GET)
     public R<List<TLbt>> getAll() {
-        QueryWrapper<TLbt> wrapper = new QueryWrapper<>();
-        wrapper.lambda().orderByAsc(TLbt::getcSort);
-        List<TLbt> list = lbtService.list(wrapper);
-        return R.ok(list);
+        try {
+            QueryWrapper<TLbt> wrapper = new QueryWrapper<>();
+            wrapper.lambda().orderByAsc(TLbt::getCSort);
+            List<TLbt> list = lbtService.list(wrapper);
+            return R.ok(list);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
 
     /**
      * 添加或者更新轮播图
      *
      * @param lbt
-     * @return
+     * @return R
      */
     @Log(title = "轮播图管理", businessType = BusinessType.INSERT)
     @RequestMapping(value = "/addOrUpdate", method = RequestMethod.POST)
     @ApiOperation("添加或者更新轮播图")
     public R addOrUpdate(@RequestBody TLbt lbt) {
         try {
+            log.info("请求参数:{}", JSON.toJSONString(lbt));
             return R.ok(lbtService.addOrUpdate(lbt));
         } catch (ServiceException s) {
             log.error(s.toString());
@@ -70,21 +77,33 @@ public class TLbtController {
 
 
     /**
-     * 分页查询数据
+     * 分页查询轮播图数据
+     * @param page
+     * @param lbt
+     * @return R<Page<TLbt>>
      */
     @ApiOperation("分页查询数据")
     @RequestMapping(value = "/select", method = RequestMethod.GET)
     public R<Page<TLbt>> selectSpfl(Page<TLbt> page, TLbt lbt) {
-
-        LambdaQueryWrapper<TLbt> tLbtLambdaQueryWrapper = new LambdaQueryWrapper<>();
-
-        tLbtLambdaQueryWrapper.like(StringUtils.isNotBlank(lbt.getcDescribe()), TLbt::getcDescribe, lbt.getcDescribe())
-                .orderByAsc(TLbt::getcSort);
-        // 获取查询返回结果
-        Page<TLbt> pageSelect = lbtService.page(page, tLbtLambdaQueryWrapper);
-        return R.ok(pageSelect);
+        try {
+            LambdaQueryWrapper<TLbt> tLbtLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            tLbtLambdaQueryWrapper.like(StringUtils.isNotBlank(lbt.getCDescribe()), TLbt::getCDescribe, lbt.getCDescribe())
+                    .orderByAsc(TLbt::getCSort);
+            // 获取查询返回结果
+            Page<TLbt> pageSelect = lbtService.page(page, tLbtLambdaQueryWrapper);
+            return R.ok(pageSelect);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
 
+    /**
+     * 删除轮播图
+     *
+     * @param tLbt
+     * @return R
+     */
     @Log(title = "轮播图管理", businessType = BusinessType.DELETE)
     @RequestMapping(value = "/del", method = RequestMethod.POST)
     @ApiOperation("删除轮播图")
@@ -100,9 +119,15 @@ public class TLbtController {
         }
     }
 
+    /**
+     * 根据id查询轮播图
+     *
+     * @param tLbt
+     * @return R
+     */
     @ApiOperation("根据id查询轮播图")
     @RequestMapping(value = "/wx/getByid", method = RequestMethod.POST)
     public R getById(@RequestBody TLbt tLbt) {
-        return R.ok(lbtService.getById(tLbt.getcId()));
+        return R.ok(lbtService.getById(tLbt.getCId()));
     }
 }

+ 1 - 2
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TOrderController.java

@@ -67,7 +67,6 @@ public class TOrderController extends BaseController {
      * @param order
      * @return R<TOrder>
      */
-    //@Log(title = "新增订单", businessType = BusinessType.INSERT)
     @ApiOperation("下单")
     @RequestMapping(value = "wx/add", method = RequestMethod.POST)
     public R<TOrder> add(@RequestBody TOrder order) {
@@ -86,7 +85,7 @@ public class TOrderController extends BaseController {
      * 优惠卷核销
      *
      * @param order
-     * @return
+     * @return R<OrderVerificationVo>
      */
     @Log(title = "优惠卷核销", businessType = BusinessType.UPDATE)
     @ApiOperation("优惠卷核销")

+ 273 - 41
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WeChatController.java

@@ -2,9 +2,11 @@ package com.ylx.web.controller.massage;
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.extra.qrcode.QrCodeUtil;
 import cn.hutool.extra.qrcode.QrConfig;
 import cn.hutool.json.JSONObject;
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ylx.common.annotation.Log;
 import com.ylx.common.config.RuoYiConfig;
@@ -12,36 +14,38 @@ import com.ylx.common.constant.Constants;
 import com.ylx.common.core.controller.BaseController;
 import com.ylx.common.core.domain.AjaxResult;
 import com.ylx.common.core.domain.R;
+import com.ylx.common.core.domain.model.BindPhoneBody;
+import com.ylx.common.core.domain.model.PhoneLoginBody;
 import com.ylx.common.core.domain.model.WxLoginUser;
+import com.ylx.common.core.domain.model.aliyun.SMSVerificationCode;
+import com.ylx.common.core.domain.model.aliyun.SendSmsComponents;
+import com.ylx.common.core.domain.model.aliyun.SendSmsEnum;
 import com.ylx.common.core.redis.RedisCache;
 import com.ylx.common.enums.BusinessType;
 import com.ylx.common.utils.MessageUtils;
 import com.ylx.common.utils.StringUtils;
 import com.ylx.common.utils.file.FileUploadUtils;
-import com.ylx.common.utils.file.FileUtils;
-import com.ylx.framework.config.ServerConfig;
 import com.ylx.framework.manager.AsyncManager;
 import com.ylx.framework.manager.factory.AsyncFactory;
 import com.ylx.framework.web.service.WxTokenService;
-import com.ylx.massage.domain.CouponReceive;
 import com.ylx.massage.domain.TJs;
 import com.ylx.massage.domain.TWxUser;
-import com.ylx.massage.domain.TbFile;
 import com.ylx.massage.service.CouponReceiveService;
 import com.ylx.massage.service.TJsService;
 import com.ylx.massage.service.TWxUserService;
 import com.ylx.massage.service.TbFileService;
-import com.ylx.massage.utils.DateTimeUtils;
 import com.ylx.massage.utils.JsSignUtil;
 import com.ylx.massage.utils.StringUtilsMassage;
 import com.ylx.massage.utils.WeChatUtil;
+import com.ylx.usercenter.domain.vo.OneAccountVO;
+import com.ylx.usercenter.service.UnifiedUserCenterService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
-import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -49,7 +53,6 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.*;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLEncoder;
 import java.nio.file.Files;
@@ -74,6 +77,10 @@ public class WeChatController extends BaseController {
     private final static String REFRESH_TOKEN = "refresh_token";
     private final static String OPEN_ID = "openid";
 
+    public static final String PHONE_VERIFICATION_CODE_KEY = "sys:clientLogin:phone:";
+
+    public static final Integer PHONE_VERIFICATION_CODE_KEY_TIME = 5;
+
     /**
      * 二维码保存路径
      */
@@ -103,9 +110,27 @@ public class WeChatController extends BaseController {
 
     @Resource
     private TJsService jsService;
+
     @Autowired
     private RedisCache redisCache;
 
+    @Autowired
+    private SendSmsComponents sendSms;
+
+    /**
+     * 发送验证码开关(true:调用真实发送接口,false:验证码写死123456)
+     */
+    @Value("${ylx.sendSmsEnabled:false}")
+    private boolean sendSmsEnabled;
+
+    /**
+     * 写死的验证码(sendSmsEnabled为false时使用)
+     */
+    @Value("${ylx.fixedVerifyCode:123456}")
+    private String fixedVerifyCode;
+    @Resource
+    private UnifiedUserCenterService unifiedUserCenterService;
+
     /**
      * 微信Token验证
      *
@@ -137,29 +162,38 @@ public class WeChatController extends BaseController {
         }
     }
 
+    /**
+     * 设置服务号的菜单
+     *
+     * @return Map<?, ?>
+     */
     @GetMapping("/setMenu")
     @ApiOperation("设置菜单")
     @Log(title = "设置菜单", businessType = BusinessType.OTHER)
-    public Map<?,?> setMenu() {
-
+    public Map<?, ?> setMenu() {
         //获取access_token
         String token = weChatUtil.getToken();
         //获取的二维码ticket
         return weChatUtil.menuUtil(token);
-
     }
 
+    /**
+     * 前端获取jssdk签名
+     *
+     * @param url
+     * @return Map<?, ?>
+     */
     @GetMapping("/getSignature")
     @ApiOperation("前端获取jssdk签名")
     @Log(title = "前端获取jssdk签名", businessType = BusinessType.OTHER)
-    public Map<?,?> getSignature(String url) {
+    public Map<?, ?> getSignature(String url) {
 
         //获取access_token
         String token = weChatUtil.getToken();
         //获取jsapi_ticket
         String jsapiTicket = weChatUtil.getJsapiTicket(token);
         //生成签名
-        return jsSignUtil.sign(url,jsapiTicket);
+        return jsSignUtil.sign(url, jsapiTicket);
     }
 
 
@@ -250,7 +284,7 @@ public class WeChatController extends BaseController {
     private String getString(Map<String, String> encryptMap) throws Exception {
         // 返回消息时ToUserName的值与FromUserName的互换
         Map<String, String> returnMap = new HashMap<>();
-        String content ="欢迎来到广誉源"+"\n" +
+        String content = "欢迎来到广誉源" + "\n" +
                 "\n" +
                 "广誉源是一家快速上门服务预约平台,提供正规 绿色 快捷上门服务,提供按摩、推拿、养生、SPA等服务,专业针对居家、差旅、酒店等顾客提供便捷健康养生服务";
         //添加新用户
@@ -296,16 +330,9 @@ public class WeChatController extends BaseController {
     @Log(title = "获取微信code", businessType = BusinessType.OTHER)
     @GetMapping("/getCode")
     public String weiXinLogin(String state) {
-//        QrConfig config = new QrConfig(300, 300);
-        // 设置边距,即二维码和背景之间的边距
-//        config.setMargin(1);
-        // 生成二维码到文件,也可以到流
         String code = weChatUtil.getCode(state);
         log.info("code的值:{}", code);
         redisCache.setCacheObject("code", state, 10, TimeUnit.MINUTES);
-
-//        QrCodeUtil.generate(code, config,
-//                FileUtil.file(IMG_PATH));
         return code;
     }
 
@@ -313,13 +340,11 @@ public class WeChatController extends BaseController {
      * 获取token和userInfo
      *
      * @param code 微信授权码
-     * @return 访问令牌
+     * @return R<WxLoginUser> 访问令牌
      */
     @GetMapping("/getAccessToken")
     @ApiOperation("公众号网页登录")
-    @Log(title = "公众号网页登录", businessType = BusinessType.OTHER)
     public R<WxLoginUser> getAccessToken(@RequestParam String code) {
-
         // 发送get请求获取 AccessToken
         Map<?, ?> result = weChatUtil.getAccessToken(code);
         String accessToken = result.get(ACCESS_TOKEN).toString();
@@ -331,24 +356,16 @@ public class WeChatController extends BaseController {
         String userInfoJsom = weChatUtil.getUserInfo(accessToken, openid);
         // 解析JSON数据
         JSONObject jsonObject = new JSONObject(userInfoJsom);
-        log.info("公众号网页登录,{}",jsonObject);
+        log.info("公众号网页登录:{}", jsonObject);
         // 将用户信息保存到数据库中
         LambdaQueryWrapper<TWxUser> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
         objectLambdaQueryWrapper.eq(TWxUser::getcOpenid, openid);
         TWxUser user = wxUserService.getOne(objectLambdaQueryWrapper);
         if (user == null || StringUtils.isEmpty(user.getcNickName())) {
-            if(user == null){
-                user = new TWxUser();
-                user.setcOpenid(openid);
-                TWxUser finalUser = user;
-                //异步 添加新人优惠卷
-//                threadPoolTaskExecutor.submit(() -> couponReceiveService.submit(new CouponReceive().setOpenid(finalUser.getcOpenid()).setCouponId("1")));
-            }
             user.setcOpenid(openid);
             user.setcNickName(jsonObject.get("nickname").toString());
             user.setcIcon(jsonObject.get("headimgurl").toString());
             user.setcSessionKey(refreshToken);
-//            user.setcPhone(phoneNumber);
             wxUserService.saveOrUpdate(user);
             user.setId(user.getId());
         }
@@ -357,6 +374,7 @@ public class WeChatController extends BaseController {
         BeanUtils.copyProperties(user, wxUser);
         // 生成并返回令牌
         String token = wxTokenService.createToken(wxUser);
+        log.info("生成的token值:{}", token);
         if (token == null || token.isEmpty()) {
             return R.fail("生成令牌失败");
         }
@@ -391,14 +409,24 @@ public class WeChatController extends BaseController {
         return accessToken;
     }
 
-    @ApiOperation("获取公众号二维码")
+    /**
+     * 获取服务号二维码
+     *
+     * @param openId
+     * @return Map<?, ?> 包含二维码ticket的Map
+     */
+    @ApiOperation("获取服务号二维码")
     @RequestMapping(value = "getwxQrCode", method = RequestMethod.GET)
     public Map<?, ?> getWxQrCodeUtil(@RequestParam String openId) {
-
-        //获取access_token
-        String token = weChatUtil.getToken();
-        //获取的二维码ticket
-        return weChatUtil.getTicket(token, openId);
+        try {
+            //获取access_token
+            String token = weChatUtil.getToken();
+            //获取的二维码ticket
+            return weChatUtil.getTicket(token, openId);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
 
     @ApiOperation("获取JS公众号二维码ticket")
@@ -431,10 +459,10 @@ public class WeChatController extends BaseController {
      */
     @ApiOperation("兑换技师公众号二维码")
     @RequestMapping(value = "getJSwxQrCodeDh", method = RequestMethod.GET)
-    public String getJSwxQrCode1(@RequestParam String ticket,@RequestParam String id) throws UnsupportedEncodingException {
+    public String getJSwxQrCode1(@RequestParam String ticket, @RequestParam String id) throws UnsupportedEncodingException {
 
         String qrCodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + URLEncoder.encode(ticket, "UTF-8");
-        String filePath = RuoYiConfig.getUploadEwmPath() + id +".png";
+        String filePath = RuoYiConfig.getUploadEwmPath() + id + ".png";
         log.info("二维码地址:{}", filePath);
 
         try (InputStream in = new URL(qrCodeUrl).openStream();
@@ -453,7 +481,6 @@ public class WeChatController extends BaseController {
     }
 
 
-
     @ApiOperation("获取公众号网页二维码")
     @GetMapping("/getweQrCode")
     public AjaxResult weiXinLogin1(String openId) {
@@ -469,4 +496,209 @@ public class WeChatController extends BaseController {
         MultipartFile multipartFile = FileUploadUtils.getMultipartFile(generate);
         return tbFileService.uploadFile(multipartFile);
     }
+
+    /**
+     * 发送验证码
+     *
+     * @param phone 手机号
+     * @return R<?> 发送结果
+     */
+    @ApiOperation("发送绑定手机号验证码")
+    @PostMapping("/sendBindPhoneCode")
+    public R<?> sendBindPhoneCode(@RequestParam String phone) {
+        if (StringUtils.isEmpty(phone)) {
+            return R.fail("手机号不能为空");
+        }
+        if (!phone.matches("^1[3-9]\\d{9}$")) {
+            return R.fail("手机号格式不正确");
+        }
+        // 根据开关判断是否调用真实发送接口
+        if (sendSmsEnabled) {
+            // TODO: 调用真实发送验证码接口
+            log.info("发送验证码到手机号: {}", phone);
+        } else {
+            // 使用写死的验证码
+            log.info("发送验证码开关关闭,使用写死验证码: {}, 手机号: {}", fixedVerifyCode, phone);
+        }
+        return R.ok("验证码发送成功");
+    }
+
+    /**
+     * 绑定手机号
+     *
+     * @param bindPhoneBody 绑定手机号请求参数
+     * @return R<?> 绑定结果
+     */
+    @ApiOperation("绑定手机号")
+    @PostMapping("/bindPhone")
+    public R<?> bindPhone(@RequestBody BindPhoneBody bindPhoneBody) {
+        String openId = bindPhoneBody.getOpenId();
+        String phone = bindPhoneBody.getPhone();
+        String code = bindPhoneBody.getCode();
+
+        if (StringUtils.isEmpty(openId)) {
+            return R.fail("openId不能为空");
+        }
+        if (StringUtils.isEmpty(phone)) {
+            return R.fail("手机号不能为空");
+        }
+        if (StringUtils.isEmpty(code)) {
+            return R.fail("验证码不能为空");
+        }
+        if (!phone.matches("^1[3-9]\\d{9}$")) {
+            return R.fail("手机号格式不正确");
+        }
+
+        // 校验验证码
+        if (!validateVerifyCode(phone, code)) {
+            return R.fail("验证码错误");
+        }
+
+        // 查询当前用户
+        TWxUser currentUser = wxUserService.getByOpenId(openId);
+        if (currentUser == null) {
+            return R.fail("用户不存在");
+        }
+
+        // 检查手机号是否已被其他用户绑定
+        TWxUser existUser = wxUserService.getByPhone(phone);
+        if (existUser != null && !existUser.getId().equals(currentUser.getId())) {
+            return R.fail("手机号已被其他用户绑定");
+        }
+
+        // 如果该用户已经绑定过此手机号,直接返回成功(无变化)
+        if (StringUtils.isNotEmpty(currentUser.getcPhone()) && currentUser.getcPhone().equals(phone)) {
+            return R.ok("手机号已绑定,无需重复绑定");
+        }
+
+        // 绑定手机号
+        boolean success = wxUserService.bindPhone(openId, phone);
+        if (!success) {
+            return R.fail("绑定失败");
+        }
+        log.info("用户openId: {} 绑定手机号: {} 成功", openId, phone);
+        return R.ok("绑定成功");
+    }
+
+    /**
+     * 校验验证码
+     *
+     * @param phone 手机号
+     * @param code  用户输入的验证码
+     * @return boolean 校验是否通过
+     */
+    private boolean validateVerifyCode(String phone, String code) {
+        if (sendSmsEnabled) {
+            // TODO: 从Redis或其他存储中获取真实发送的验证码进行校验
+            // String realCode = redisCache.getCacheObject("sms:bindPhone:" + phone);
+            // return code.equals(realCode);
+            return true;
+        } else {
+            // 使用写死的验证码进行校验
+            return fixedVerifyCode.equals(code);
+        }
+    }
+
+    /**
+     * 手机号登录
+     *
+     * @param phoneLoginBody 手机号登录请求参数
+     * @return R<WxLoginUser> 登录结果
+     */
+    @ApiOperation("手机号登录")
+    @PostMapping("/phoneLogin")
+    //@Log(title = "手机号登录", businessType = BusinessType.OTHER)
+    public R<WxLoginUser> phoneLogin(@RequestBody PhoneLoginBody phoneLoginBody) {
+        String phone = phoneLoginBody.getPhone();
+        String code = phoneLoginBody.getCode();
+
+        // 校验验证码(验证码不为空且为6位数字在@Valid注解中已校验)
+        if (StringUtils.isEmpty(code)) {
+            return R.fail("验证码不能为空");
+        }
+
+        // 手机号登录获取用户
+        TWxUser user = wxUserService.phoneLogin(phone);
+        if (user == null) {
+            return R.fail("登录失败");
+        }
+
+        // 构建WxLoginUser
+        WxLoginUser wxUser = new WxLoginUser();
+        BeanUtils.copyProperties(user, wxUser);
+
+        // 生成并返回令牌
+        String token = wxTokenService.createToken(wxUser);
+        log.info("手机号登录生成的token值:{}", token);
+        if (StringUtils.isEmpty(token)) {
+            return R.fail("生成令牌失败");
+        }
+        wxUser.setToken(token);
+        // 记录登录信息
+        AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
+        log.info("手机号: {} 登录成功", phone);
+        return R.ok(wxUser);
+    }
+
+    /**
+     * 发送短信验证码
+     *
+     * @param phone 手机号
+     * @return R<String> 发送结果
+     */
+    @GetMapping("/sendMsg")
+    @ApiOperation(value = "发送短信验证码", notes = "发送短信验证码")
+    public R<String> sendMsg(@RequestParam String phone, HttpServletRequest request) {
+        if (org.apache.commons.lang3.StringUtils.isEmpty(phone)) {
+            return R.fail("手机号不能为空");
+        }
+        Random rand = new Random();
+        // randNumber 将被赋值为一个 MIN 和 MAX 范围内的随机数
+        int randNumber = rand.nextInt(9999 - 1000 + 1) + 1000;
+        // 保存验证码到redis
+        redisCache.setCacheObject(PHONE_VERIFICATION_CODE_KEY + phone, String.valueOf(randNumber), PHONE_VERIFICATION_CODE_KEY_TIME, TimeUnit.MINUTES);
+        try {
+            SMSVerificationCode smsVerificationCode = new SMSVerificationCode(String.valueOf(randNumber));
+            String jsonString = JSON.toJSONString(smsVerificationCode);
+            sendSms.sendSms(phone, SendSmsEnum.SMS_220650023, jsonString);
+            return R.ok("发送成功");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return R.fail("发送失败");
+    }
+
+    @ApiOperation("uuid登录")
+    @PostMapping("/uuidLogin")
+    @Log(title = "uuid登录", businessType = BusinessType.OTHER)
+    public R<WxLoginUser> uuidLogin(@RequestBody PhoneLoginBody phoneLoginBody) {
+        String uuid = phoneLoginBody.getUuid();
+
+        if (StringUtils.isEmpty(uuid)) {
+            return R.fail("uuid不能为空");
+        }
+        OneAccountVO oneAccountVO = this.unifiedUserCenterService.queryClients(phoneLoginBody.getUuid());
+        if (ObjUtil.isNull(oneAccountVO)) {
+            return R.fail("未查询到用户信息");
+        }
+        TWxUser user = wxUserService.getById(oneAccountVO.getUserId());
+        if (ObjUtil.isNull(user)) {
+            return R.fail("登录失败");
+        }
+        // 构建WxLoginUser
+        WxLoginUser wxUser = new WxLoginUser();
+        BeanUtils.copyProperties(user, wxUser);
+        // 生成并返回令牌
+        String token = wxTokenService.createToken(wxUser);
+        log.info("uuid登录生成的token值:{}", token);
+        if (StringUtils.isEmpty(token)) {
+            return R.fail("生成令牌失败");
+        }
+        wxUser.setToken(token);
+        // 记录登录信息
+        AsyncManager.me().execute(AsyncFactory.recordLogininfor(uuid, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
+        log.info("uuid: {} 登录成功", uuid);
+        return R.ok(wxUser);
+    }
+
 }

+ 8 - 8
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WeSqController.java

@@ -27,6 +27,7 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.*;
+
 import javax.annotation.Resource;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
@@ -45,7 +46,6 @@ public class WeSqController extends BaseController {
     private final static String OPEN_ID = "openid";
 
 
-
     @Resource
     private WeChatUtil weChatUtil;
 
@@ -66,7 +66,7 @@ public class WeSqController extends BaseController {
 
 
     /**
-     * 获取token和userInfo
+     * 通过微信code获取token和userInfo
      *
      * @param code 微信授权码
      * @return R<WxLoginUser> 访问令牌
@@ -89,24 +89,24 @@ public class WeSqController extends BaseController {
             String userInfoJsom = weChatUtil.getUserInfo(accessToken, openid);
             // 解析JSON数据
             JSONObject jsonObject = new JSONObject(userInfoJsom);
-            log.info("公众号网页登录:{}",jsonObject);
+            log.info("公众号网页登录:{}", jsonObject);
             // 将用户信息保存到数据库中
             LambdaQueryWrapper<TWxUser> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
             objectLambdaQueryWrapper.eq(TWxUser::getcOpenid, openid);
             TWxUser user = wxUserService.getOne(objectLambdaQueryWrapper);
             if (user == null || StringUtils.isEmpty(user.getcNickName())) {
-                if(user == null){
+                if (user == null) {
                     user = new TWxUser();
                     user.setcOpenid(openid);
                     TWxUser finalUser = user;
                     //异步 添加新人优惠卷
-    //                threadPoolTaskExecutor.submit(() -> couponReceiveService.submit(new CouponReceive().setOpenid(finalUser.getcOpenid()).setCouponId("1")));
+                    //                threadPoolTaskExecutor.submit(() -> couponReceiveService.submit(new CouponReceive().setOpenid(finalUser.getcOpenid()).setCouponId("1")));
                 }
                 user.setcOpenid(openid);
                 user.setcNickName(jsonObject.get("nickname").toString());
                 user.setcIcon(jsonObject.get("headimgurl").toString());
                 user.setcSessionKey(refreshToken);
-    //            user.setcPhone(phoneNumber);
+                //            user.setcPhone(phoneNumber);
                 wxUserService.saveOrUpdate(user);
                 user.setId(user.getId());
             }
@@ -116,11 +116,11 @@ public class WeSqController extends BaseController {
             // 生成并返回令牌
             String token = wxTokenService.createToken(wxUser);
             log.info("token的值:{}", token);
-            //给我把token的值保存到redis中
-            redisTemplate.opsForValue().set(wxUser.getCOpenid(), token, 180, TimeUnit.MINUTES);
             if (token == null || token.isEmpty()) {
                 return R.fail("生成令牌失败");
             }
+            //给我把token的值保存到redis中
+            redisTemplate.opsForValue().set(wxUser.getCOpenid(), token, 180, TimeUnit.MINUTES);
             wxUser.setToken(token);
             // 返回用户信息
             // 记录登录信息

+ 2 - 8
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WxController.java

@@ -99,16 +99,10 @@ public class WxController extends BaseController {
     /**
      * 获取二维码
      *
-     * @param param
-     * @return
+     * @param inviteUserId
+     * @return AjaxResult
      */
     @ApiOperation("获取二维码")
-//    @RequestMapping(value = "getwxQrCode", method = RequestMethod.POST)
-//    public String getWxQrCodeUtil(@RequestBody JSONObject param) throws IOException {
-//        String cOpenId = param.getString("cOpenId");
-//        return wxQrCodeUtil.generateQRCode(null,null,null);
-//    }
-    //生成小程序码方式 ,produces = MediaType.IMAGE_JPEG_VALUE
     @PostMapping(value="getwxQrCode")
     public AjaxResult inviteCode(@RequestParam ("inviteUserId")String inviteUserId) throws WxErrorException, IOException {
         File file = wxMaService.getQrcodeService().createWxaCodeUnlimit(inviteUserId, null, 300, true, null, true);

+ 146 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/point/PointActivityController.java

@@ -0,0 +1,146 @@
+package com.ylx.web.controller.point;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.annotation.Log;
+import com.ylx.common.core.controller.BaseController;
+import com.ylx.common.core.domain.AjaxResult;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.enums.BusinessType;
+import com.ylx.point.domain.PointActivity;
+import com.ylx.point.domain.dto.PointActivityDTO;
+import com.ylx.point.domain.dto.PointActivityPageDTO;
+import com.ylx.point.domain.dto.PointActivityValidityDTO;
+import com.ylx.point.domain.dto.SignDTO;
+import com.ylx.point.domain.vo.*;
+import com.ylx.point.service.IPointActivityService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 积分活动主Controller
+ *
+ * @author wzj
+ * @date 2026-03-26
+ */
+@RestController
+@RequestMapping("/point/activity")
+@Api(tags = {"积分规则配置"})
+@Slf4j
+public class PointActivityController extends BaseController {
+
+    @Autowired
+    private IPointActivityService pointActivityService;
+
+    /**
+     * 查询积分活动主列表
+     */
+    @PreAuthorize("@ss.hasPermi('point:activity:list')")
+    @ApiOperation("根据条件分页查询积分活动")
+    @GetMapping("/page")
+    public R<Page<PointActivityPageVo>> page(Page<PointActivity> page, PointActivityPageDTO dto) {
+        Page<PointActivityPageVo> pageData = this.pointActivityService.list(page, dto);
+        return R.ok(pageData);
+    }
+
+    /**
+     * 获取积分活动主详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('point:activity:query')")
+    @ApiOperation("根据id获取积分活动详情")
+    @GetMapping(value = "/{id}")
+    public R<PointActivityDetailsVo<?>> getDetailsInfo(@PathVariable("id") Long id) {
+        PointActivityDetailsVo<?> vo = this.pointActivityService.getDetailsInfo(id);
+        return R.ok(vo);
+    }
+
+    /**
+     * 新增积分活动
+     */
+    @PreAuthorize("@ss.hasPermi('point:activity:add')")
+    @ApiOperation("新增积分活动")
+    @Log(title = "积分活动添加", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody PointActivityDTO dto) {
+        try {
+            this.pointActivityService.add(dto);
+            return success();
+        } catch (Exception e) {
+            log.error("新增积分活动失败", e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 修改积分活动主
+     */
+    @PreAuthorize("@ss.hasPermi('point:activity:edit')")
+    @ApiOperation("编辑积分活动")
+    @Log(title = "积分活动编辑", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody PointActivityDTO dto) {
+        this.pointActivityService.edit(dto);
+        return success();
+    }
+
+    /**
+     * 获取积分活动主详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('point:activity:query')")
+    @ApiOperation("根据id获取积分活动统计数据")
+    @GetMapping(value = "/stat/{id}")
+    public R<PointActivityStatVo> getStatInfo(@PathVariable("id") Long id) {
+        PointActivityStatVo vo = this.pointActivityService.getStatInfo(id);
+        return R.ok(vo);
+    }
+
+    /**
+     * 修改积分活动主
+     */
+    @PreAuthorize("@ss.hasPermi('point:activity:edit')")
+    @ApiOperation("编辑积分活动时效")
+    @Log(title = "积分活动时效编辑", businessType = BusinessType.UPDATE)
+    @PutMapping(value = "/validity")
+    public AjaxResult editValidity(@Validated @RequestBody PointActivityValidityDTO dto) {
+        this.pointActivityService.editValidity(dto);
+        return success();
+    }
+
+    /**
+     * 修改积分活动主
+     */
+    @PreAuthorize("@ss.hasPermi('point:activity:query')")
+    @ApiOperation("积分活动时效回显")
+    @GetMapping(value = "/validity/details")
+    public R<List<PointActivityValidityVo>> validityDetails() {
+        List<PointActivityValidityVo> list = this.pointActivityService.validityDetails();
+        return R.ok(list);
+    }
+
+
+    @ApiOperation("获取签到面板数据")
+    @PostMapping(value = "/sign/info")
+    public R<List<SignDayVo>> getSignInfo(@Validated @RequestBody SignDTO dto) {
+        List<SignDayVo> list = this.pointActivityService.getSignInfo(dto);
+        return R.ok(list);
+    }
+
+    /**
+     * 用户签到接口
+     * @param dto
+     * @return R
+     */
+    @ApiOperation("用户签到接口")
+    @PostMapping(value = "/sign")
+    public R sign(@Validated @RequestBody SignDTO dto) {
+        this.pointActivityService.sign(dto);
+        return R.ok();
+    }
+
+}

+ 88 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/point/UserPointController.java

@@ -0,0 +1,88 @@
+package com.ylx.web.controller.point;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.core.controller.BaseController;
+import com.ylx.common.core.domain.R;
+import com.ylx.point.domain.PointActivity;
+import com.ylx.point.domain.PointUserLog;
+import com.ylx.point.domain.dto.CompleteTaskDTO;
+import com.ylx.point.domain.dto.UserPointActivityPageDTO;
+import com.ylx.point.domain.dto.UserPointPageDTO;
+import com.ylx.point.domain.vo.UserPointActivityVo;
+import com.ylx.point.domain.vo.UserPointInfoVO;
+import com.ylx.point.domain.vo.UserPointLogVO;
+import com.ylx.point.service.IPointAccountService;
+import com.ylx.point.service.IPointActivityService;
+import com.ylx.point.service.IPointUserActivityTaskCompletionService;
+import com.ylx.point.service.IPointUserLogService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@RestController
+@RequestMapping("/user/point")
+@Api(tags = {"用户积分"})
+public class UserPointController extends BaseController {
+
+    @Resource
+    private IPointUserLogService pointUserLogService;
+    @Resource
+    private IPointActivityService pointActivityService;
+    @Resource
+    private IPointUserActivityTaskCompletionService pointUserActivityTaskCompletionService;
+    @Resource
+    private IPointAccountService pointAccountService;
+
+    /**
+     * 获取当前用户的积分信息
+     *
+     * @param cityCode
+     * @return R<UserPointInfoVO>
+     */
+    @ApiOperation("获取当前用户的积分信息")
+    @GetMapping
+    public R<UserPointInfoVO> getUserPointInfo(@RequestParam String cityCode) {
+        UserPointInfoVO vo = this.pointUserLogService.getUserPointInfo(cityCode);
+        return R.ok(vo);
+    }
+
+    @ApiOperation("获取当前用户的积分信息分页数据")
+    @PostMapping("/page")
+    public R<Page<UserPointLogVO>> page(Page<PointUserLog> page, @RequestBody UserPointPageDTO dto) {
+        Page<UserPointLogVO> pageData = pointUserLogService.getUserPointLogList(page, dto);
+        return R.ok(pageData);
+    }
+
+    @ApiOperation("获取当前用户活动任务分页数据")
+    @PostMapping("/activity/page")
+    public R<Page<UserPointActivityVo>> activityPage(Page<PointActivity> page, @RequestBody UserPointActivityPageDTO dto) {
+        Page<UserPointActivityVo> pageData = pointActivityService.getUserPointActivityList(page, dto);
+        return R.ok(pageData);
+    }
+
+    @ApiOperation("获取当前用户做任务赚积分集合")
+    @GetMapping("/activity/list")
+    public R<List<UserPointActivityVo>> activityList() {
+        List<UserPointActivityVo> list = pointActivityService.activityList();
+        return R.ok(list);
+    }
+
+    @ApiOperation("用户点击“去完成”接口")
+    @PostMapping("/complete")
+    public R completeTask(@Validated @RequestBody CompleteTaskDTO dto) {
+        this.pointUserActivityTaskCompletionService.executeTask(dto.getTaskId());
+        return R.ok();
+    }
+
+    @ApiOperation("用户领取积分")
+    @PostMapping("/draw")
+    public R draw() {
+        this.pointAccountService.draw();
+        return R.ok();
+    }
+
+}

+ 50 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/usercenter/UnifiedUserCenterController.java

@@ -0,0 +1,50 @@
+package com.ylx.web.controller.usercenter;
+
+import com.ylx.common.core.controller.BaseController;
+import com.ylx.common.core.domain.R;
+import com.ylx.usercenter.domain.dto.UnifiedUserCenterDTO;
+import com.ylx.usercenter.service.UnifiedUserCenterService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping("/userCenter")
+@Api(tags = {"一账通客户端"})
+@Slf4j
+public class UnifiedUserCenterController extends BaseController {
+
+    @Resource
+    private UnifiedUserCenterService unifiedUserCenterService;
+
+    @ApiOperation("查询用户绑定")
+    @PostMapping("/userApp/queryBind")
+    public R<?> queryBind(@RequestBody UnifiedUserCenterDTO dto) {
+        return this.unifiedUserCenterService.queryBind(dto);
+    }
+
+    @ApiOperation("添加用户绑定")
+    @PostMapping("/userApp/bind")
+    public R<?> bind(@RequestBody UnifiedUserCenterDTO dto) {
+        return this.unifiedUserCenterService.bind(dto);
+    }
+
+    @ApiOperation("解除用户绑定")
+    @PostMapping("/userApp/unbind")
+    public R<?> unbind(@RequestBody UnifiedUserCenterDTO dto) {
+        return this.unifiedUserCenterService.unbind(dto);
+    }
+
+    @ApiOperation("用户绑定更新")
+    @PostMapping("/userApp/bind/update")
+    public R<?> bindUpdate(@RequestBody UnifiedUserCenterDTO dto) {
+        return this.unifiedUserCenterService.bindUpdate(dto);
+    }
+
+}

+ 1 - 3
nightFragrance-admin/src/main/resources/application.yml

@@ -1,5 +1,3 @@
 spring:
   profiles:
-    active: dev
-#  profiles:
-#    active: druid
+    active: dev

+ 33 - 33
nightFragrance-admin/src/main/resources/i18n/messages.properties

@@ -1,38 +1,38 @@
-#错误消息
-not.null=* 必须填写
-user.jcaptcha.error=验证码错误
-user.jcaptcha.expire=验证码已失效
-user.not.exists=用户不存在/密码错误
-user.password.not.match=用户不存在/密码错误
-user.password.retry.limit.count=密码输入错误{0}次
-user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
-user.password.delete=对不起,您的账号已被删除
-user.blocked=用户已封禁,请联系管理员
-role.blocked=角色已封禁,请联系管理员
-login.blocked=很遗憾,访问IP已被列入系统黑名单
-user.logout.success=退出成功
+#\u9519\u8BEF\u6D88\u606F
+not.null=* \u5FC5\u987B\u586B\u5199
+user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF
+user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548
+user.not.exists=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF
+user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF
+user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
+user.password.retry.limit.exceed=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
+user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\u5DF2\u88AB\u5220\u9664
+user.blocked=\u7528\u6237\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+login.blocked=\u5F88\u9057\u61BE\uFF0C\u8BBF\u95EEIP\u5DF2\u88AB\u5217\u5165\u7CFB\u7EDF\u9ED1\u540D\u5355
+user.logout.success=\u9000\u51FA\u6210\u529F
 
-length.not.valid=长度必须在{min}到{max}个字符之间
+length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
 
-user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
-user.password.not.valid=* 5-50个字符
+user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934
+user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
 
-user.email.not.valid=邮箱格式错误
-user.mobile.phone.number.not.valid=手机号格式错误
-user.login.success=登录成功
-user.register.success=注册成功
-user.notfound=请重新登录
-user.forcelogout=管理员强制退出,请重新登录
-user.unknown.error=未知错误,请重新登录
+user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
+user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
+user.login.success=\u767B\u5F55\u6210\u529F
+user.register.success=\u6CE8\u518C\u6210\u529F
+user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55
+user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
+user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
 
-##文件上传消息
-upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
-upload.filename.exceed.length=上传的文件名最长{0}个字符
+##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
+upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01
+upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26
 
-##权限
-no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
-no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
-no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
-no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
-no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
-no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
+##\u6743\u9650
+no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]

+ 6 - 0
nightFragrance-common/pom.xml

@@ -200,6 +200,12 @@
             <artifactId>spring-boot-configuration-processor</artifactId>
             <optional>true</optional>
         </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+            <version>3.1.0</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 27 - 0
nightFragrance-common/src/main/java/com/ylx/common/annotation/EnumTranslate.java

@@ -0,0 +1,27 @@
+package com.ylx.common.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({FIELD})
+@Retention(RUNTIME)
+public @interface EnumTranslate {
+
+    /**
+     * 枚举类
+     */
+    Class<? extends Enum<?>> enumClass();
+
+    /**
+     * 枚举中获取code的方法名
+     */
+    String codeMethod() default "getCode";
+
+    /**
+     * 枚举中获取描述的方法名
+     */
+    String descMethod() default "getInfo";
+}

+ 4 - 5
nightFragrance-common/src/main/java/com/ylx/common/annotation/Log.java

@@ -5,6 +5,7 @@ import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+
 import com.ylx.common.enums.BusinessType;
 import com.ylx.common.enums.OperatorType;
 
@@ -12,20 +13,18 @@ import com.ylx.common.enums.OperatorType;
  * 自定义操作日志记录注解
  *
  * @author ylx
- *
  */
-@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Target({ElementType.PARAMETER, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
-public @interface Log
-{
+public @interface Log {
     /**
      * 模块
      */
     public String title() default "";
 
     /**
-     * 功能
+     * 业务操作类型
      */
     public BusinessType businessType() default BusinessType.OTHER;
 

+ 119 - 0
nightFragrance-common/src/main/java/com/ylx/common/aspect/EnumTranslateAspect.java

@@ -0,0 +1,119 @@
+package com.ylx.common.aspect;
+
+import com.ylx.common.annotation.EnumTranslate;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+
+@Aspect
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE + 1)
+public class EnumTranslateAspect {
+
+    // 【改成你自己的 controller 包路径】
+    @Pointcut("execution(public * com.ylx.web.controller..*.*(..))")
+    public void pointcut() {}
+
+    @Around("pointcut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        Object result = point.proceed();
+        if (result == null) return null;
+
+        // 全局翻译:支持 单个对象 / List / 分页对象
+        translate(result);
+        return result;
+    }
+
+    // ==========================================
+    // 核心方法:递归翻译所有类型
+    // ==========================================
+    private void translate(Object obj) {
+        if (obj == null) return;
+
+        // 1. 集合 List Set
+        if (obj instanceof Collection) {
+            ((Collection<?>) obj).forEach(this::translate);
+            return;
+        }
+
+        // 2. 分页对象(这里是关键!自动提取分页里的 rows)
+        Class<?> clazz = obj.getClass();
+        try {
+            // 通用分页字段:rows / list / data 三个都兼容
+            for (String fieldName : Arrays.asList("rows", "list", "data")) {
+                Field listField = clazz.getDeclaredField(fieldName);
+                listField.setAccessible(true);
+                Object listObj = listField.get(obj);
+                if (listObj instanceof Collection) {
+                    translate(listObj);
+                }
+            }
+        } catch (NoSuchFieldException ignored) {
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // 3. 翻译当前对象自身的字段
+        translateFields(obj);
+    }
+
+    // ==========================================
+    // 翻译单个对象的枚举字段
+    // ==========================================
+    private void translateFields(Object obj) {
+        Field[] fields = obj.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            EnumTranslate anno = field.getAnnotation(EnumTranslate.class);
+            if (anno == null) continue;
+
+            try {
+                field.setAccessible(true);
+                Object code = field.get(obj);
+                if (code == null) continue;
+
+                // 拿到枚举描述
+                String desc = getEnumDesc(
+                        anno.enumClass(),
+                        code,
+                        anno.codeMethod(),
+                        anno.descMethod()
+                );
+
+                // 自动赋值给 statusDesc
+                Field descField = obj.getClass().getDeclaredField(field.getName() + "Desc");
+                descField.setAccessible(true);
+                descField.set(obj, desc);
+            } catch (NoSuchFieldException ignored) {
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    // ==========================================
+    // 根据code获取枚举描述
+    // ==========================================
+    private String getEnumDesc(Class<? extends Enum<?>> enumClass, Object code, String codeMethod, String descMethod) {
+        try {
+            Method getCode = enumClass.getMethod(codeMethod);
+            Method getDesc = enumClass.getMethod(descMethod);
+
+            for (Enum<?> e : enumClass.getEnumConstants()) {
+                Object enumCode = getCode.invoke(e);
+                if (enumCode.equals(code)) {
+                    return String.valueOf(getDesc.invoke(e));
+                }
+            }
+        } catch (Exception ignored) {}
+        return "";
+    }
+}

+ 14 - 0
nightFragrance-common/src/main/java/com/ylx/common/config/AmapConfig.java

@@ -0,0 +1,14 @@
+package com.ylx.common.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "amap")
+public class AmapConfig {
+
+    private String apiKey;
+
+}

+ 1 - 1
nightFragrance-common/src/main/java/com/ylx/common/config/WechatAccountConfig.java

@@ -34,7 +34,7 @@ public class WechatAccountConfig {
     private String redirectUrl;
 
     /**
-     * 回调地址,需要修改域名,修改http://1dcd46bb.r24.cpolar.top这个就好啦
+     * 获取微信AccessToken的URL
      */
     private String accessTokenUrl;
 

+ 1 - 1
nightFragrance-common/src/main/java/com/ylx/common/config/WxPayConfig.java

@@ -50,7 +50,7 @@ public class WxPayConfig {
     private String certPath;
 
     /**
-     * 证书钥地址
+     * 证书钥地址
      */
     private String certKeyPath;
 

+ 4 - 0
nightFragrance-common/src/main/java/com/ylx/common/core/domain/BaseEntity.java

@@ -5,6 +5,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
@@ -21,6 +22,7 @@ public class BaseEntity implements Serializable {
      * 搜索值
      */
     @JsonIgnore
+    @TableField(exist = false)
     private String searchValue;
 
     /**
@@ -48,12 +50,14 @@ public class BaseEntity implements Serializable {
     /**
      * 备注
      */
+    @TableField(exist = false)
     private String remark;
 
     /**
      * 请求参数
      */
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @TableField(exist = false)
     private Map<String, Object> params;
 
     public String getSearchValue() {

+ 29 - 0
nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/BindPhoneBody.java

@@ -0,0 +1,29 @@
+package com.ylx.common.core.domain.model;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 绑定手机号请求类
+ *
+ * @author ylx
+ */
+@ApiModel(description = "绑定手机号请求")
+@Data
+public class BindPhoneBody {
+
+    @NotBlank(message = "openId不能为空")
+    @ApiModelProperty(value = "用户的openId", required = true)
+    private String openId;
+
+    @NotBlank(message = "手机号不能为空")
+    @ApiModelProperty(value = "手机号", required = true)
+    private String phone;
+
+    @NotBlank(message = "验证码不能为空")
+    @ApiModelProperty(value = "验证码", required = true)
+    private String code;
+}

+ 31 - 0
nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/PhoneLoginBody.java

@@ -0,0 +1,31 @@
+package com.ylx.common.core.domain.model;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+
+/**
+ * 手机号登录请求类
+ *
+ * @author ylx
+ */
+@ApiModel(description = "手机号登录请求")
+@Data
+public class PhoneLoginBody {
+
+    @NotBlank(message = "手机号不能为空")
+    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
+    @ApiModelProperty(value = "手机号", required = true)
+    private String phone;
+
+    @NotBlank(message = "验证码不能为空")
+    @Pattern(regexp = "^\\d{6}$", message = "验证码必须为6位数字")
+    @ApiModelProperty(value = "验证码(6位数字)", required = true)
+    private String code;
+
+    private String uuid;
+
+}

+ 12 - 1
nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/WxLoginUser.java

@@ -9,6 +9,9 @@ import org.springframework.security.core.userdetails.UserDetails;
 import java.math.BigDecimal;
 import java.util.Collection;
 
+/**
+ * 微信登录用户信息
+ */
 @Data
 public class WxLoginUser implements UserDetails {
 
@@ -19,7 +22,9 @@ public class WxLoginUser implements UserDetails {
      */
     private String token;
 
-
+    /**
+     * 用户id
+     */
     private String id;
 
     /**
@@ -137,6 +142,12 @@ public class WxLoginUser implements UserDetails {
      */
     private String cMrAddressId;
 
+    @ApiModelProperty("是否绑定一账通 0否 1是")
+    private Integer isBind;
+
+    @ApiModelProperty("本地生活用户id")
+    private String localLiveUserId;
+
     @Override
     public Collection<? extends GrantedAuthority> getAuthorities() {
         return null;

+ 16 - 0
nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/AliyunApiProperties.java

@@ -0,0 +1,16 @@
+package com.ylx.common.core.domain.model.aliyun;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "aliyun.api")
+public class AliyunApiProperties {
+    //
+    private String accessKeyId;
+
+    //
+    private String accessKeySecret;
+}

+ 13 - 0
nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/SMSVerificationCode.java

@@ -0,0 +1,13 @@
+package com.ylx.common.core.domain.model.aliyun;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@AllArgsConstructor
+@Data
+public class SMSVerificationCode {
+
+    @JsonProperty(value = "code")
+    private String code;
+}

+ 66 - 0
nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/SendSmsComponents.java

@@ -0,0 +1,66 @@
+package com.ylx.common.core.domain.model.aliyun;
+
+import com.aliyun.dysmsapi20170525.Client;
+import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teaopenapi.models.Config;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class SendSmsComponents {
+    private final AliyunApiProperties properties;
+
+    /**
+     * @description: 创建请求
+     * @author: wenks
+     * @date: 2024/11/6 8:46
+     * @return:
+     **/
+    private Client createClient() throws Exception {
+        // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
+        // 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html。
+        Config config = new Config()
+                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
+                .setAccessKeyId(properties.getAccessKeyId())
+                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
+                .setAccessKeySecret(properties.getAccessKeySecret());
+        // Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
+        config.endpoint = "dysmsapi.aliyuncs.com";
+        return new Client(config);
+    }
+
+    public void sendSms(String phoneNumber, SendSmsEnum sendSmsEnum, String templateParam) throws Exception {
+        Client client = createClient();
+        SendSmsRequest sendSmsRequest = new SendSmsRequest()
+                .setPhoneNumbers(phoneNumber)
+                .setSignName(sendSmsEnum.getSignName())
+                .setTemplateCode(sendSmsEnum.getCode())
+                .setTemplateParam(templateParam);
+
+        sendSmsData(client, sendSmsRequest);
+    }
+
+    private void sendSmsData(Client client, SendSmsRequest sendSmsRequest) {
+        try {
+            // 复制代码运行请自行打印 API 的返回值
+            client.sendSms(sendSmsRequest);
+        } catch (TeaException error) {
+            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
+            // 错误 message
+            log.info(error.getMessage());
+            // 诊断地址
+            log.info((String) error.getData().get("Recommend"));
+        } catch (Exception _error) {
+            TeaException error = new TeaException(_error.getMessage(), _error);
+            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
+            // 错误 message
+            log.info(error.getMessage());
+            // 诊断地址
+            log.info((String) error.getData().get("Recommend"));
+        }
+    }
+}

+ 17 - 0
nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/SendSmsEnum.java

@@ -0,0 +1,17 @@
+package com.ylx.common.core.domain.model.aliyun;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum SendSmsEnum {
+
+    SMS_220650023("SMS_220650023", "山西掌柜鼎科技", "验证码短信");
+
+    private final String code;
+
+    private final String signName;
+
+    private final String desc;
+}

+ 3 - 6
nightFragrance-common/src/main/java/com/ylx/common/core/page/TableSupport.java

@@ -8,8 +8,7 @@ import com.ylx.common.utils.ServletUtils;
  *
  * @author ylx
  */
-public class TableSupport
-{
+public class TableSupport {
     /**
      * 当前记录起始索引
      */
@@ -38,8 +37,7 @@ public class TableSupport
     /**
      * 封装分页对象
      */
-    public static PageDomain getPageDomain()
-    {
+    public static PageDomain getPageDomain() {
         PageDomain pageDomain = new PageDomain();
         pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
         pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
@@ -49,8 +47,7 @@ public class TableSupport
         return pageDomain;
     }
 
-    public static PageDomain buildPageRequest()
-    {
+    public static PageDomain buildPageRequest() {
         return getPageDomain();
     }
 }

+ 1 - 3
nightFragrance-common/src/main/java/com/ylx/common/enums/BusinessStatus.java

@@ -4,10 +4,8 @@ package com.ylx.common.enums;
  * 操作状态
  *
  * @author ylx
- *
  */
-public enum BusinessStatus
-{
+public enum BusinessStatus {
     /**
      * 成功
      */

+ 1 - 2
nightFragrance-common/src/main/java/com/ylx/common/enums/BusinessType.java

@@ -5,8 +5,7 @@ package com.ylx.common.enums;
  *
  * @author ylx
  */
-public enum BusinessType
-{
+public enum BusinessType {
     /**
      * 其它
      */

+ 3 - 6
nightFragrance-common/src/main/java/com/ylx/common/utils/PageUtils.java

@@ -10,13 +10,11 @@ import com.ylx.common.utils.sql.SqlUtil;
  *
  * @author ylx
  */
-public class PageUtils extends PageHelper
-{
+public class PageUtils extends PageHelper {
     /**
      * 设置请求分页数据
      */
-    public static void startPage()
-    {
+    public static void startPage() {
         PageDomain pageDomain = TableSupport.buildPageRequest();
         Integer pageNum = pageDomain.getPageNum();
         Integer pageSize = pageDomain.getPageSize();
@@ -28,8 +26,7 @@ public class PageUtils extends PageHelper
     /**
      * 清理分页的线程变量
      */
-    public static void clearPage()
-    {
+    public static void clearPage() {
         PageHelper.clearPage();
     }
 }

+ 5 - 2
nightFragrance-common/src/main/java/com/ylx/common/utils/SecurityUtils.java

@@ -59,7 +59,7 @@ public class SecurityUtils {
     }
 
     /**
-     * 获取用户
+     * 获取用户(PC端)
      *
      * @return LoginUser 用户信息
      **/
@@ -67,7 +67,6 @@ public class SecurityUtils {
         try {
             Object principal = getAuthentication().getPrincipal();
             log.info("principal的值: {}", principal);
-
             return (LoginUser) getAuthentication().getPrincipal();
         } catch (Exception e) {
             e.printStackTrace();
@@ -77,6 +76,8 @@ public class SecurityUtils {
 
     /**
      * 获取微信用户
+     *
+     * @return WxLoginUser
      **/
     public static WxLoginUser getWxLoginUser() {
         try {
@@ -88,6 +89,8 @@ public class SecurityUtils {
 
     /**
      * 获取Authentication
+     *
+     * @return Authentication
      */
     public static Authentication getAuthentication() {
         return SecurityContextHolder.getContext().getAuthentication();

+ 7 - 0
nightFragrance-framework/src/main/java/com/ylx/framework/aspectj/LogAspect.java

@@ -87,6 +87,13 @@ public class LogAspect {
         handleLog(joinPoint, controllerLog, e, null);
     }
 
+    /**
+     * 处理日志记录
+     * @param joinPoint
+     * @param controllerLog
+     * @param e
+     * @param jsonResult
+     */
     protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
         try {
             // 获取当前的用户

+ 26 - 0
nightFragrance-framework/src/main/java/com/ylx/framework/config/MyMetaObjectHandler.java

@@ -0,0 +1,26 @@
+package com.ylx.framework.config;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+@Component
+@Slf4j
+public class MyMetaObjectHandler implements MetaObjectHandler {
+
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        log.info("开始插入填充");
+        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
+        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+    }
+
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        log.info("开始更新填充");
+        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+    }
+}

+ 12 - 15
nightFragrance-framework/src/main/java/com/ylx/framework/config/SecurityConfig.java

@@ -26,8 +26,7 @@ import com.ylx.framework.security.handle.LogoutSuccessHandlerImpl;
  * @author ylx
  */
 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
-public class SecurityConfig extends WebSecurityConfigurerAdapter
-{
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
     /**
      * 自定义用户认证逻辑
      */
@@ -72,8 +71,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
      */
     @Bean
     @Override
-    public AuthenticationManager authenticationManagerBean() throws Exception
-    {
+    public AuthenticationManager authenticationManagerBean() throws Exception {
         return super.authenticationManagerBean();
     }
 
@@ -93,8 +91,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
      * authenticated       |   用户登录后可访问
      */
     @Override
-    protected void configure(HttpSecurity httpSecurity) throws Exception
-    {
+    protected void configure(HttpSecurity httpSecurity) throws Exception {
         // 注解标记允许匿名访问的url
         ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
         permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
@@ -111,13 +108,15 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 // 过滤请求
                 .authorizeRequests()
                 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
-                .antMatchers("/login", "/register", "/captchaImage","/wx/login",
+                .antMatchers("/login", "/register", "/captchaImage", "/wx/login",
                         "/api/lbt/v1/getAll", "/api/js/v1/select", "/api/xiangmu/v1/wx/getAll", "/api/order/v1/getStatus",
-                        "/api/xiangmu/v1/getByid", "/api/xiangmu/v1/highlights","/api/js/v1/wx/getByid","/api/js/v1/wx/select","/api/js/v1/wx/add", "/api/recharge/v1/test",
-                        "/wx/pay/payNotify","/wx/pay/refundNotify","/weChat/getAccessToken","/weChat/getCode","/weChat/verifyToken","/sq/getAccessToken",
-                        "/area/select","/system/dept/list","/api/xiangmu/v1/wx/recommend").permitAll()
+                        "/api/xiangmu/v1/getByid", "/api/xiangmu/v1/highlights", "/api/js/v1/wx/getByid", "/api/js/v1/wx/select", "/api/js/v1/wx/add", "/api/recharge/v1/test",
+                        "/wx/pay/payNotify", "/wx/pay/refundNotify", "/weChat/getAccessToken","/weChat/phoneLogin","/weChat/sendMsg", "/weChat/getCode", "/weChat/verifyToken", "/sq/getAccessToken",
+                        "/area/select", "/system/dept/list", "/api/xiangmu/v1/wx/recommend", "/product/category/create","/area/code","/area/city","/product/category/list",
+                        "/wx/pay/query/order/{outTradeNo}","/api/products/**","/api/user/point/**","/userCenter/userApp/queryBind","/weChat/uuidLogin","/couponReceive/getShareVolutionDetail",
+                        "/userCenter/userApp/bind/update").permitAll()
                 // 静态资源,可匿名访问
-                .antMatchers(HttpMethod.GET, "/", "/*.txt","/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
+                .antMatchers(HttpMethod.GET, "/", "/*.txt", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
@@ -136,8 +135,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
      * 强散列哈希加密实现
      */
     @Bean
-    public BCryptPasswordEncoder bCryptPasswordEncoder()
-    {
+    public BCryptPasswordEncoder bCryptPasswordEncoder() {
         return new BCryptPasswordEncoder();
     }
 
@@ -145,8 +143,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
      * 身份认证接口
      */
     @Override
-    protected void configure(AuthenticationManagerBuilder auth) throws Exception
-    {
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
     }
 }

+ 1 - 2
nightFragrance-framework/src/main/java/com/ylx/framework/security/handle/AuthenticationEntryPointImpl.java

@@ -24,8 +24,7 @@ public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, S
     private static final long serialVersionUID = -8970718410437077606L;
 
     @Override
-    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
-            throws IOException {
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
         int code = HttpStatus.UNAUTHORIZED;
         String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
         ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));

+ 8 - 6
nightFragrance-framework/src/main/java/com/ylx/framework/security/handle/LogoutSuccessHandlerImpl.java

@@ -42,26 +42,28 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
     /**
      * 退出处理
      *
-     * @return
+     * @param request
+     * @param response
+     * @param authentication
      */
     @Override
-    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
-            throws IOException, ServletException {
-
+    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+        log.info("用户退出登录");
         LoginUser loginUser = tokenService.getLoginUser(request);
         if (StringUtils.isNotNull(loginUser)) {
+            log.info("PC用户退出登录");
             String userName = loginUser.getUsername();
             // 删除用户缓存记录
             tokenService.delLoginUser(loginUser.getToken());
             // 记录用户退出日志
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
         }else{
+            log.info("微信用户退出登录");
             WxLoginUser wxUser = wxTokenService.getWxUser(request);
             if (StringUtils.isNotNull(wxUser)) {
-                log.info("微信用户退出登录:{}", JSONUtil.toJsonStr(wxUser));
-                String userName = wxUser.getUsername();
                 // 删除用户缓存记录
                 wxTokenService.delWxUser(wxUser.getToken());
+                String userName = wxUser.getUsername();
                 // 记录用户退出日志
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
             }

+ 24 - 45
nightFragrance-framework/src/main/java/com/ylx/framework/web/service/PermissionService.java

@@ -1,6 +1,7 @@
 package com.ylx.framework.web.service;
 
 import java.util.Set;
+
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 import com.ylx.common.constant.Constants;
@@ -16,23 +17,19 @@ import com.ylx.framework.security.context.PermissionContextHolder;
  * @author ylx
  */
 @Service("ss")
-public class PermissionService
-{
+public class PermissionService {
     /**
      * 验证用户是否具备某权限
      *
      * @param permission 权限字符串
      * @return 用户是否具备某权限
      */
-    public boolean hasPermi(String permission)
-    {
-        if (StringUtils.isEmpty(permission))
-        {
+    public boolean hasPermi(String permission) {
+        if (StringUtils.isEmpty(permission)) {
             return false;
         }
         LoginUser loginUser = SecurityUtils.getLoginUser();
-        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
-        {
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
             return false;
         }
         PermissionContextHolder.setContext(permission);
@@ -45,8 +42,7 @@ public class PermissionService
      * @param permission 权限字符串
      * @return 用户是否不具备某权限
      */
-    public boolean lacksPermi(String permission)
-    {
+    public boolean lacksPermi(String permission) {
         return hasPermi(permission) != true;
     }
 
@@ -56,23 +52,18 @@ public class PermissionService
      * @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表
      * @return 用户是否具有以下任意一个权限
      */
-    public boolean hasAnyPermi(String permissions)
-    {
-        if (StringUtils.isEmpty(permissions))
-        {
+    public boolean hasAnyPermi(String permissions) {
+        if (StringUtils.isEmpty(permissions)) {
             return false;
         }
         LoginUser loginUser = SecurityUtils.getLoginUser();
-        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
-        {
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
             return false;
         }
         PermissionContextHolder.setContext(permissions);
         Set<String> authorities = loginUser.getPermissions();
-        for (String permission : permissions.split(Constants.PERMISSION_DELIMETER))
-        {
-            if (permission != null && hasPermissions(authorities, permission))
-            {
+        for (String permission : permissions.split(Constants.PERMISSION_DELIMETER)) {
+            if (permission != null && hasPermissions(authorities, permission)) {
                 return true;
             }
         }
@@ -85,22 +76,17 @@ public class PermissionService
      * @param role 角色字符串
      * @return 用户是否具备某角色
      */
-    public boolean hasRole(String role)
-    {
-        if (StringUtils.isEmpty(role))
-        {
+    public boolean hasRole(String role) {
+        if (StringUtils.isEmpty(role)) {
             return false;
         }
         LoginUser loginUser = SecurityUtils.getLoginUser();
-        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
-        {
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
             return false;
         }
-        for (SysRole sysRole : loginUser.getUser().getRoles())
-        {
+        for (SysRole sysRole : loginUser.getUser().getRoles()) {
             String roleKey = sysRole.getRoleKey();
-            if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
-            {
+            if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) {
                 return true;
             }
         }
@@ -113,8 +99,7 @@ public class PermissionService
      * @param role 角色名称
      * @return 用户是否不具备某角色
      */
-    public boolean lacksRole(String role)
-    {
+    public boolean lacksRole(String role) {
         return hasRole(role) != true;
     }
 
@@ -124,21 +109,16 @@ public class PermissionService
      * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
      * @return 用户是否具有以下任意一个角色
      */
-    public boolean hasAnyRoles(String roles)
-    {
-        if (StringUtils.isEmpty(roles))
-        {
+    public boolean hasAnyRoles(String roles) {
+        if (StringUtils.isEmpty(roles)) {
             return false;
         }
         LoginUser loginUser = SecurityUtils.getLoginUser();
-        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
-        {
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
             return false;
         }
-        for (String role : roles.split(Constants.ROLE_DELIMETER))
-        {
-            if (hasRole(role))
-            {
+        for (String role : roles.split(Constants.ROLE_DELIMETER)) {
+            if (hasRole(role)) {
                 return true;
             }
         }
@@ -149,11 +129,10 @@ public class PermissionService
      * 判断是否包含权限
      *
      * @param permissions 权限列表
-     * @param permission 权限字符串
+     * @param permission  权限字符串
      * @return 用户是否具备某权限
      */
-    private boolean hasPermissions(Set<String> permissions, String permission)
-    {
+    private boolean hasPermissions(Set<String> permissions, String permission) {
         return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
     }
 }

+ 2 - 1
nightFragrance-framework/src/main/java/com/ylx/framework/web/service/TokenService.java

@@ -62,6 +62,7 @@ public class TokenService {
     public LoginUser getLoginUser(HttpServletRequest request) {
         // 获取请求携带的令牌
         String token = getToken(request);
+        log.info("token的值:{}", token);
         if (StringUtils.isNotEmpty(token)) {
             try {
                 Claims claims = parseToken(token);
@@ -199,7 +200,7 @@ public class TokenService {
      * 获取请求token
      *
      * @param request
-     * @return token
+     * @return String token
      */
     private String getToken(HttpServletRequest request) {
         String token = request.getHeader(header);

+ 1 - 0
nightFragrance-massage/pom.xml

@@ -31,6 +31,7 @@
             <artifactId>IJPay-WxPay</artifactId>
             <version>2.9.10</version>
         </dependency>
+
         <dependency>
             <groupId>com.github.wechatpay-apiv3</groupId>
             <artifactId>wechatpay-java</artifactId>

+ 56 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/LotteryCountLog.java

@@ -0,0 +1,56 @@
+package com.ylx.lottery.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ylx.common.annotation.Excel;
+import com.ylx.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LotteryCountLog extends BaseEntity {
+    private static final long serialVersionUID = -7870788541798529977L;
+
+    private Long id;
+
+    @Excel(name = "用户的openID")
+    private String openId;
+
+    @Excel(name = "用户的userID")
+    private String userId;
+
+    @Excel(name = "用户电话")
+    private String userPhone;
+
+    @Excel(name = "活动类型:1.推广拉新 2.任务奖励  3.消费奖励")
+    private Integer activityType;
+
+    @Excel(name = "本地活动表ID")
+    private String localActivityTableId;
+
+    @Excel(name = "获得抽奖次数")
+    private Integer lotteryNum;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @Excel(name = "领取时间", dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date receiveTime;
+
+    @Excel(name = "是否删除 (1:是, 0:否)")
+    private String isDelete;
+
+    @Excel(name = "同步状态 0-未同步到本地生活 1-已同步到本地生活")
+    private Integer status;
+
+    @Excel(name = "类型(1.按摩订单 2.积分订单 3.评论有礼)")
+    private Integer type;
+
+    @Excel(name = "是否弹窗抽奖(0 .没有 1.有)")
+    private Integer isLottery;
+
+    @ApiModelProperty("本地活动链接url")
+    private String activityUrl;
+
+}

+ 18 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/dto/IsLotteryDTO.java

@@ -0,0 +1,18 @@
+package com.ylx.lottery.domain.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class IsLotteryDTO implements Serializable {
+    private static final long serialVersionUID = -8430222377384281190L;
+
+    @NotNull(message = "类型不能为空")
+    @ApiModelProperty(value = "类型:1.按摩订单 2.积分订单 3.评论有礼", required = true)
+    private List<Integer> types;
+
+}

+ 24 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/dto/LotteryCountDTO.java

@@ -0,0 +1,24 @@
+package com.ylx.lottery.domain.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 抽奖次数增加请求DTO
+ */
+@Data
+public class LotteryCountDTO implements Serializable {
+    private static final long serialVersionUID = -39721081196232178L;
+
+    @ApiModelProperty(value = "客户id")
+    private String appUserId;
+
+    @ApiModelProperty(value = "抽奖次数")
+    private Integer count;
+
+    @ApiModelProperty(value = "活动id")
+    private String activityId;
+
+}

+ 22 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/IsLotteryVO.java

@@ -0,0 +1,22 @@
+package com.ylx.lottery.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class IsLotteryVO implements Serializable {
+
+    private static final long serialVersionUID = -8430222377384281190L;
+
+    @ApiModelProperty("是否可以抽奖:0.没有 1有")
+    private Integer isLottery;
+
+    @ApiModelProperty("本地活动表ID")
+    private String activityId;
+
+    @ApiModelProperty("本地活动url")
+    private String activityUrl;
+
+}

+ 36 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LocalActivityTableVO.java

@@ -0,0 +1,36 @@
+package com.ylx.lottery.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class LocalActivityTableVO implements Serializable {
+    private static final long serialVersionUID = -6758927198878789658L;
+
+    @ApiModelProperty("ID")
+    private String id;
+
+    @ApiModelProperty(value = "积分数量")
+    private Integer pointNumber;
+
+    @ApiModelProperty("类型 6-报价 7-签到 8-出单 9-承保")
+    private String type;
+
+    @ApiModelProperty(value = "是否是商品商户 1 商品 2 商户")
+    private String isProduct;
+
+    @ApiModelProperty(value = "指定商品id")
+    private String productId;
+
+    @ApiModelProperty(value = "每日最大抽奖次数")
+    private String qutLotteryMax;
+
+    @ApiModelProperty(value = "活动链接")
+    private String activityUrl;
+
+    @ApiModelProperty(value = "活动id")
+    private String activityId;
+
+}

+ 21 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryActivityRulesProductVO.java

@@ -0,0 +1,21 @@
+package com.ylx.lottery.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class LotteryActivityRulesProductVO implements Serializable {
+    private static final long serialVersionUID = -6758927198878789658L;
+
+    @ApiModelProperty(value = "主键")
+    private String id;
+
+    @ApiModelProperty(value = "活动id")
+    private String activityId;
+
+    @ApiModelProperty(value = "商品id")
+    private String productId;
+
+}

+ 45 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryActivityVO.java

@@ -0,0 +1,45 @@
+package com.ylx.lottery.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class LotteryActivityVO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("活动ID")
+    private String id;
+
+    @ApiModelProperty("参与规则")
+    private Integer participationRules;
+
+    @ApiModelProperty(value = "消费奖励 1商品限制 2消费限制")
+    private Integer productRestriction;
+
+    @ApiModelProperty(value = "消费奖励 购买指定商品得几次抽奖机会")
+    private Integer productLotteryNum;
+
+    @ApiModelProperty(value = "消费奖励 每个用户最多得几次")
+    private Integer productUserMaxNum;
+
+    @ApiModelProperty(value = "消费奖励 消费满几元")
+    private Integer consumeAmount;
+
+    @ApiModelProperty(value = "消费奖励 消费满几元的几次抽奖机会")
+    private Integer consumeAmountLottery;
+
+    @ApiModelProperty("本地活动链接url")
+    private String activityUrl;
+
+    @ApiModelProperty(value = "商品")
+    private List<LotteryActivityRulesProductVO> lotteryActivityRulesProducts;
+
+    @ApiModelProperty("本地活动表")
+    private List<LocalActivityTableVO> localActivityTables;
+
+    @ApiModelProperty(value = "广誉源积分活动")
+    private List<LocalActivityTableVO> gyyPointTables;
+}

+ 36 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryCountResponseVO.java

@@ -0,0 +1,36 @@
+package com.ylx.lottery.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class LotteryCountResponseVO<L> implements Serializable {
+    private static final long serialVersionUID = 3315449915058182289L;
+
+    /**
+     * 请求是否成功
+     */
+    private boolean success;
+
+    /**
+     * 状态码 (例如: 200表示成功)
+     */
+    private Integer code;
+
+    /**
+     * 响应消息/提示信息
+     */
+    private String message;
+
+    /**
+     * 具体的业务结果数据
+     * 此处示例为String,实际开发中可根据需要改为泛型 <T>
+     */
+    private L result;
+
+    /**
+     * 服务器响应时间戳
+     */
+    private Long timestamp;
+}

+ 11 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/domain/vo/LotteryStatVO.java

@@ -0,0 +1,11 @@
+package com.ylx.lottery.domain.vo;
+
+import lombok.Data;
+
+@Data
+public class LotteryStatVO {
+
+    private String localActivityTableId; // 对应分组的字段
+
+    private Integer totalNum;            // 对应求和的字段
+}

+ 20 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/mapper/LotteryCountLogMapper.java

@@ -0,0 +1,20 @@
+package com.ylx.lottery.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.ylx.lottery.domain.LotteryCountLog;
+import com.ylx.lottery.domain.vo.LotteryStatVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+public interface LotteryCountLogMapper extends BaseMapper<LotteryCountLog> {
+
+    @Select("SELECT local_activity_table_id, SUM(lottery_num) as totalNum " +
+            "FROM lottery_count_log " +
+            "${ew.customSqlSegment} " +
+            "GROUP BY local_activity_table_id")
+    List<LotteryStatVO> selectSumByGroup(@Param(Constants.WRAPPER) Wrapper wrapper);
+}

+ 17 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/service/LotteryCountLogService.java

@@ -0,0 +1,17 @@
+package com.ylx.lottery.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ylx.lottery.domain.LotteryCountLog;
+import com.ylx.lottery.domain.dto.IsLotteryDTO;
+import com.ylx.lottery.domain.vo.IsLotteryVO;
+import com.ylx.lottery.domain.vo.LotteryStatVO;
+
+import java.util.List;
+
+public interface LotteryCountLogService extends IService<LotteryCountLog> {
+
+    List<LotteryStatVO> selectSumByGroup(LambdaQueryWrapper<LotteryCountLog> queryWrapper);
+
+    IsLotteryVO isLottery(IsLotteryDTO dto);
+}

+ 14 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/service/LotteryCountService.java

@@ -0,0 +1,14 @@
+package com.ylx.lottery.service;
+
+import com.ylx.lottery.domain.vo.LotteryActivityVO;
+import com.ylx.usercenter.domain.dto.UnifiedUserCenterDTO;
+
+import java.util.List;
+
+public interface LotteryCountService {
+
+    boolean batchAdd(UnifiedUserCenterDTO dto);
+
+    List<LotteryActivityVO>  queryActivityRules();
+
+}

+ 87 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/service/impl/LotteryCountLogServiceImpl.java

@@ -0,0 +1,87 @@
+package com.ylx.lottery.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ylx.common.core.domain.model.WxLoginUser;
+import com.ylx.common.exception.ServiceException;
+import com.ylx.common.utils.SecurityUtils;
+import com.ylx.lottery.domain.LotteryCountLog;
+import com.ylx.lottery.domain.dto.IsLotteryDTO;
+import com.ylx.lottery.domain.vo.IsLotteryVO;
+import com.ylx.lottery.domain.vo.LotteryStatVO;
+import com.ylx.lottery.mapper.LotteryCountLogMapper;
+import com.ylx.lottery.service.LotteryCountLogService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class LotteryCountLogServiceImpl extends ServiceImpl<LotteryCountLogMapper, LotteryCountLog> implements LotteryCountLogService {
+
+    @Override
+    public List<LotteryStatVO> selectSumByGroup(LambdaQueryWrapper<LotteryCountLog> queryWrapper) {
+        return this.baseMapper.selectSumByGroup(queryWrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public IsLotteryVO isLottery(IsLotteryDTO dto) {
+
+        IsLotteryVO vo = new IsLotteryVO();
+
+        // 1. 获取当前登录用户
+        WxLoginUser loginUser = SecurityUtils.getWxLoginUser();
+        if (ObjectUtil.isNull(loginUser)) {
+            throw new ServiceException("用户未登录或登录已过期");
+        }
+
+        String userId = loginUser.getId();
+        if (StrUtil.isBlank(userId)) {
+            throw new ServiceException("用户ID不合法");
+        }
+
+        // 2. 构建查询条件 (用于查找未弹窗的记录)
+        LambdaQueryWrapper<LotteryCountLog> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LotteryCountLog::getUserId, userId)
+                .eq(LotteryCountLog::getIsLottery, 0) // 只查未弹窗的
+                .eq(LotteryCountLog::getIsDelete, 0)
+                .in(LotteryCountLog::getType, dto.getTypes());
+
+        // 3.提取列表
+        List<LotteryCountLog> lotteryCountLogs = this.baseMapper.selectList(queryWrapper);
+
+        // 4. 设置返回结果
+        if (CollUtil.isEmpty(lotteryCountLogs)) {
+            vo.setIsLottery(0);
+            return vo;
+        }
+
+        LotteryCountLog lotteryCountLog = CollUtil.getFirst(lotteryCountLogs);
+
+        vo.setIsLottery(1);
+        vo.setActivityId(lotteryCountLog.getLocalActivityTableId());
+        vo.setActivityUrl(lotteryCountLog.getActivityUrl());
+
+        // 5. 如果有记录,批量更新状态为“已弹窗”
+        if (CollUtil.isNotEmpty(lotteryCountLogs)) {
+
+            LambdaUpdateWrapper<LotteryCountLog> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.in(LotteryCountLog::getType, dto.getTypes())
+                    .eq(LotteryCountLog::getUserId, userId)
+                    .set(LotteryCountLog::getIsLottery, 1)
+                    .set(LotteryCountLog::getUpdateTime, new Date());
+
+            this.update(updateWrapper);
+        }
+
+        return vo;
+    }
+
+}

+ 177 - 0
nightFragrance-massage/src/main/java/com/ylx/lottery/service/impl/LotteryCountServiceImpl.java

@@ -0,0 +1,177 @@
+package com.ylx.lottery.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.ylx.common.exception.ServiceException;
+import com.ylx.lottery.domain.LotteryCountLog;
+import com.ylx.lottery.domain.dto.LotteryCountDTO;
+import com.ylx.lottery.domain.vo.LotteryActivityVO;
+import com.ylx.lottery.domain.vo.LotteryCountResponseVO;
+import com.ylx.lottery.domain.vo.LotteryStatVO;
+import com.ylx.lottery.service.LotteryCountLogService;
+import com.ylx.lottery.service.LotteryCountService;
+import com.ylx.usercenter.domain.dto.UnifiedUserCenterDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import com.alibaba.fastjson.TypeReference;
+
+import javax.annotation.Resource;
+import java.util.*;
+
+@Slf4j
+@Service
+public class LotteryCountServiceImpl implements LotteryCountService {
+
+    @Value("${remote.local-live.base-url}")
+    private String localLiveBaseUrl;
+
+    // 定义接口路径常量
+    private static final String LOTTERY_COUNT_ADD_PATH = "/lottery/lotteryUserLotteryCount/batchAdd";
+    private static final String LOTTERY_ACTIVITY_LIST_PATH = "/lottery/lotteryActivity/queryActivityList";
+    private static final int SCENE_CODE = 2;
+    private static final int DEFAULT_TIMEOUT = 5000; // 5秒超时
+
+    @Resource
+    private LotteryCountLogService lotteryCountLogService;
+
+    @Override
+    public boolean batchAdd(UnifiedUserCenterDTO dto) {
+
+        List<LotteryCountDTO> dtoList = new ArrayList<>();
+        // 1. 获取广誉源用户本地未同步抽奖次数
+        LambdaQueryWrapper<LotteryCountLog> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LotteryCountLog::getUserId, dto.getTargetUserId());
+        queryWrapper.eq(LotteryCountLog::getStatus, 0);
+        queryWrapper.eq(LotteryCountLog::getIsDelete, 0);
+        List<LotteryStatVO> resultList = lotteryCountLogService.selectSumByGroup(queryWrapper);
+
+        if (CollUtil.isEmpty(resultList)) {
+            log.info("用户[{}]没有待同步的抽奖记录", dto.getTargetUserId());
+            return true;
+        }
+
+        // 2. 构建请求参数
+        for (LotteryStatVO statVO : resultList) {
+            LotteryCountDTO countDTO = new LotteryCountDTO();
+            countDTO.setAppUserId(dto.getSourceUserId());
+            countDTO.setCount(statVO.getTotalNum());
+            countDTO.setActivityId(statVO.getLocalActivityTableId());
+            dtoList.add(countDTO);
+        }
+
+        // 3. 调用接口同步抽奖次数
+        try {
+            // 1. 构建 URL
+            String url = localLiveBaseUrl + LOTTERY_COUNT_ADD_PATH;
+
+            // 2. 转换参数
+            String jsonParam = JSON.toJSONString(dtoList);
+
+            // 3. 发送请求
+            String result = HttpUtil.post(url, jsonParam, DEFAULT_TIMEOUT);
+
+            // 4. 校验结果
+            if (StrUtil.isEmpty(result)) {
+                throw new RuntimeException("抽奖次数批量添加接口失败:接口返回空结果");
+            }
+
+            // 5. 解析结果
+            LotteryCountResponseVO<String> response = JSON.parseObject(
+                    result,
+                    new TypeReference<LotteryCountResponseVO<String>>() {
+                    }
+            );
+
+            // 4. 处理业务状态
+            if (!response.isSuccess()) {
+                log.warn("抽奖次数批量添加接口失败失败: code={}, msg={}", response.getCode(), response.getMessage());
+                throw new ServiceException("远程接口返回失败: " + response.getMessage());
+            }
+
+            // 5. 更新本地数据库状态
+            updateSyncStatus(dto.getTargetUserId());
+
+            return true;
+
+        } catch (Exception e) {
+            log.error("抽奖次数批量添加接口发生异常", e);
+            throw new ServiceException("抽奖次数批量添加接口异常" + e);
+        }
+
+    }
+
+    @Override
+    public List<LotteryActivityVO> queryActivityRules() {
+        try {
+            // 1. 构建请求参数
+            String url = localLiveBaseUrl + LOTTERY_ACTIVITY_LIST_PATH;
+            Map<String, Object> params = Collections.singletonMap("scene", SCENE_CODE);
+
+            // 2. 发送请求
+            String resultJson = HttpUtil.get(url, params, DEFAULT_TIMEOUT);
+
+            // 3. 校验结果
+            if (StrUtil.isEmpty(resultJson)) {
+                throw new RuntimeException("抽奖规则查询接口失败:接口返回空结果");
+            }
+
+            // 4. 解析结果
+            LotteryCountResponseVO<List<LotteryActivityVO>> response = JSON.parseObject(
+                    resultJson,
+                    new TypeReference<LotteryCountResponseVO<List<LotteryActivityVO>>>() {
+                    }
+            );
+
+            // 5. 处理业务状态
+            if (ObjUtil.isNull(response) || !response.isSuccess()) {
+                String msg = ObjUtil.isNotNull(response) ? response.getMessage() : "未知错误";
+                Integer code = ObjUtil.isNotNull(response) ? response.getCode() : -1;
+
+                log.warn("抽奖规则查询接口业务失败: code={}, msg={}", code, msg);
+                throw new ServiceException("远程接口返回失败: " + msg);
+            }
+
+            // 6. 获取数据列表
+            List<LotteryActivityVO> activityList = response.getResult();
+
+            if (CollUtil.isEmpty(activityList)) {
+                log.info("未查询到相关活动数据");
+                return Collections.emptyList();
+            }
+
+            log.info("成功获取抽奖活动列表,数量: {}", activityList.size());
+
+            return activityList;
+
+        } catch (ServiceException e) {
+            // 透传业务异常,不要重复包装
+            throw e;
+        } catch (Exception e) {
+            log.error("抽奖规则查询接口发生系统异常", e);
+            // 优化点:只抛出消息,不要直接把整个 Exception 对象 toString() 拼接到字符串里
+            throw new ServiceException("抽奖规则查询接口异常: " + e.getMessage());
+        }
+    }
+
+    private void updateSyncStatus(String userId) {
+        LambdaUpdateWrapper<LotteryCountLog> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(LotteryCountLog::getUserId, userId)
+                .eq(LotteryCountLog::getStatus, 0) // 只更新未同步的
+                .eq(LotteryCountLog::getIsDelete, 0)
+                .set(LotteryCountLog::getStatus, 1); // 1-已同步
+
+        boolean updateResult = lotteryCountLogService.update(updateWrapper);
+        if (!updateResult) {
+            log.warn("用户[{}]抽奖记录状态更新失败", userId);
+        } else {
+            log.info("用户[{}]抽奖记录状态更新成功", userId);
+        }
+    }
+
+}

+ 117 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/AfterSaleOrder.java

@@ -0,0 +1,117 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("after_sale_order")
+public class AfterSaleOrder implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /** 售后单号(唯一) */
+    private String afterSaleNo;
+
+    /** 原订单ID */
+    private Long orderId;
+
+    /** 原订单编号 */
+    private String orderNo;
+
+    /** 用户openId */
+    private String openId;
+
+    /**
+     * 售后类型
+     * 1退货 2换货 3:未收到货
+     */
+    private Integer afterSaleType;
+
+    /**
+     * 售后状态
+     * 0待审核 1审核通过 2审核拒绝 3待买家退货 4待商家收货 5退款处理中 6售后完成
+     */
+    private Integer afterSaleStatus;
+
+    /** 申请原因 */
+    private String applyReason;
+
+    /** 问题描述 */
+    private String applyDesc;
+
+    /** 凭证图片URL,多个逗号分隔(最多3张) */
+    private String applyImages;
+
+    /** 申请退款金额 */
+    private Integer applyAmount;
+
+    /** 实际退款金额 */
+    private Integer actualRefundAmount;
+
+    /**
+     * 退款方式
+     * 1原路退回 2退至余额
+     */
+    private Integer refundType;
+
+    /** 审核人ID */
+    private Long auditUserId;
+
+    /** 审核时间 */
+    private LocalDateTime auditTime;
+
+    /** 审核备注(拒绝原因等) */
+    private String auditRemark;
+
+    /**
+     * 拒绝原因
+     * 1超过售后期限 2不符合售后条件 3凭证不足 4其他
+     */
+    private Integer rejectReason;
+
+    /** 退款完成时间 */
+    private LocalDateTime refundTime;
+
+    /** 退款流水号(第三方) */
+    private String refundNo;
+
+    /** 换货新订单ID */
+    private Long exchangeOrderId;
+
+    /** 换货新订单编号 */
+    private String exchangeOrderNo;
+
+    /** 售后申请过期时间 */
+    private LocalDateTime expireTime;
+
+    /** 售后完成时间 */
+    private LocalDateTime finishTime;
+
+    /** 快递单号 */
+    private String logisticsNo;
+
+    /** 备注 */
+    private String remark;
+
+    /** 是否删除 0否 1是 */
+    @TableLogic
+    private Integer isDeleted;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}

+ 34 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/AfterSaleOrderFee.java

@@ -0,0 +1,34 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.ylx.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AfterSaleOrderFee extends BaseEntity {
+
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /** 原订单编号 */
+    private String orderNo;
+
+    /** 售后单ID */
+    private Long afterSaleOrderId;
+
+    /** 费用类型 */
+    private String type;
+
+    /** 金额 */
+    private BigDecimal fee;
+
+    /** 描述 */
+    private String remark;
+
+}

+ 5 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/domain/Area.java

@@ -26,9 +26,13 @@ public class Area extends Model<Area> {
     //编码
     @ApiModelProperty("编码")
     private String code;
-    //名称
+
+    /**
+     * 名称
+     */
     @ApiModelProperty("名称")
     private String name;
+
     //父级编码,0为顶级
     @ApiModelProperty("父级编码,0为顶级")
     private String parentCode;

+ 18 - 6
nightFragrance-massage/src/main/java/com/ylx/massage/domain/Coupon.java

@@ -36,19 +36,27 @@ public class Coupon extends Model<Coupon> {
     @ApiModelProperty("优惠券类型,1:普通券")
     private Integer type;
 
-    //优惠类型,1:无门槛, 2:折扣, 3:满减
+    /**
+     * 优惠类型, 2:折扣, 3:满减
+     */
     @ApiModelProperty("优惠类型, 2:折扣, 3:满减")
     private Integer discountType;
 
-    //优惠值
+    /**
+     * 优惠值
+     */
     @ApiModelProperty("优惠值")
     private BigDecimal discountValue;
 
-    //折扣值
+    /**
+     * 折扣值
+     */
     @ApiModelProperty("折扣值")
     private BigDecimal rebValue;
 
-    //门槛金额
+    /**
+     * 门槛金额
+     */
     @ApiModelProperty("门槛金额")
     private BigDecimal thresholdAmount;
 
@@ -56,7 +64,9 @@ public class Coupon extends Model<Coupon> {
     @ApiModelProperty("获取方式:1:手动领取 2,自动发放")
     private Integer obtainWay;
 
-    //优惠券有效期天数,0
+    /**
+     * 优惠券有效期天数
+     */
     @ApiModelProperty("优惠券有效期天数")
     private Integer termDays;
 
@@ -68,7 +78,9 @@ public class Coupon extends Model<Coupon> {
     @ApiModelProperty("已使用数量")
     private Integer usedNum;
 
-    //每个人限领的数量
+    /**
+     * 每个人限领的数量
+     */
     @ApiModelProperty("每个人限领的数量")
     private Integer userLimit;
 

+ 54 - 49
nightFragrance-massage/src/main/java/com/ylx/massage/domain/CouponReceive.java

@@ -1,79 +1,84 @@
 package com.ylx.massage.domain;
-
-
-import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.*;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.*;
 import lombok.experimental.Accessors;
-
-
 import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.Date;
-
 /**
  * 优惠券领取表(CouponReceive)表实体类
  *
  * @author makejava
  * @since 2024-05-14 11:25:44
  */
-@Setter
-@Getter
-@ApiModel(value = "CouponReceive" ,description = "优惠券领取")
-@Builder
-@Accessors(chain = true)
-@AllArgsConstructor
-@NoArgsConstructor
-public class CouponReceive extends Model<CouponReceive> {
-    //主键
-    @ApiModelProperty("id")
+
+@Data
+@TableName("coupon_receive")
+public class CouponReceive implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 主键 */
+    @TableId(type = IdType.ASSIGN_ID)
     private String id;
 
-    //用户openId
-    @ApiModelProperty("用户openId")
-    private String openid;
+    /** 发券人ID */
+    private String issuerCouponId;
 
-    //优惠券id
-    @ApiModelProperty("优惠券id")
+    /** 优惠券id */
     private String couponId;
 
-    //过期时间
-    @ApiModelProperty("过期时间")
+    /** 优惠券类型 1-兑换券 2-折扣券 3-满减券 */
+    private Integer couponType;
+
+    /** 领取用户openID */
+    private String receiveOpenId;
+
+    /** 领取时间 */
+    private LocalDateTime receiveTime;
+
+    /** 领取人手机号 */
+    private String receiveUserPhone;
+
+    /** 有效期类型 1-固定日期范围, 2-领券后N天有效, 3-长期有效,4-三方赠送 */
+    private Integer validityType;
+
+    /** 有效期开始时间 */
+    private Date validStartTime;
+
+    /** 有效期结束时间 */
     private Date expirationTime;
 
-    //过期时间
-    @ApiModelProperty("使用状态 0:待使用,1:使用中,2:已完成")
-    private Integer useState;
+    /** 有效天数 */
+    private Integer validDays;
 
-    //创建时间
-    @ApiModelProperty("创建时间")
-    private Date createTime;
+    /** 核销使用时间 */
+    private LocalDateTime useTime;
 
-    //修改时间
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
+    /** 优惠券状态 0:待使用 1:使用中 2:已完成 */
+    private Integer couponStatus;
 
-    @ApiModelProperty("部门id")
-    private String deptId;
+    /** 关联的订单ID */
+    private String orderId;
 
-    @ApiModelProperty("部门名称")
-    private String deptName;
+    /** 订单类型 1-服务 2-商品 */
+    private Integer orderType;
 
-    //是否删除0否1是
-    @ApiModelProperty("是否删除0否1是")
-    @TableLogic
-    private Integer isDelete;
+    /** 创建时间 */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
 
+    /** 修改时间 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
 
-    /**
-     * 获取主键值
-     *
-     * @return 主键值
-     */
-    @Override
-    public Serializable pkVal() {
-        return this.id;
-    }
+    /** 是否删除 0否 1是 */
+    @TableLogic
+    private Integer isDelete;
 }
 

+ 177 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/Product.java

@@ -0,0 +1,177 @@
+package com.ylx.massage.domain;
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+/**
+ * 商品表 实体类
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName(value = "product",autoResultMap = true)
+public class Product implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 商品编号(如AD2026227001)
+     */
+    @TableField("product_no")
+    private String productNo;
+
+    /**
+     * 分类ID
+     */
+    @TableField("category_id")
+    private Long categoryId;
+
+    /**
+     * 分类名称
+     */
+    @TableField(exist = false)
+    private String categoryName;
+
+    /**
+     * 商品名称
+     */
+    @TableField("name")
+    private String name;
+
+    /**
+     * 商品主图URL
+     */
+    @TableField("product_main_image")
+    private String productMainImage;
+
+    /**
+     * 商品图URL
+     */
+    @TableField(value = "product_image", typeHandler = JacksonTypeHandler.class)
+    private List<String> productImage;
+
+    /**
+     * 商品简述
+     */
+    @TableField("summary")
+    private String summary;
+
+    /**
+     * 商品详情(富文本HTML)
+     */
+    @TableField("detail")
+    private String detail;
+
+    /**
+     * 付款类型(1:积分 2:金额 3:积分+金额)
+     */
+    @TableField("payment_type")
+    private Integer paymentType;
+
+    /**
+     * 积分价格
+     */
+    @TableField("price_point")
+    private Integer pricePoint;
+
+    /**
+     * 现金价格(元)
+     */
+    @TableField("price_money")
+    private BigDecimal priceMoney;
+
+    /**
+     * 原价
+     */
+    @TableField(exist = false)
+    private BigDecimal originalPrice;
+
+    /**
+     * 总库存(冗余,由SKU汇总)
+     */
+    @TableField("stock")
+    private Integer stock;
+
+    /**
+     * 已售数量
+     */
+    @TableField("sales")
+    private Integer sales;
+
+    /**
+     * 基础运费
+     */
+    @TableField("freight")
+    private BigDecimal freight;
+
+    /**
+     * 承诺发货时间(如48小时)
+     */
+    @TableField("delivery_time")
+    private String deliveryTime;
+
+    /**
+     * 服务承诺,JSON数组存储
+     * 例如:["假一赔三", "7天无理由退换", "破损包退换"]
+     */
+    @TableField(value = "service_promise", typeHandler = JacksonTypeHandler.class)
+    private List<String> servicePromise;
+
+    /**
+     * 上架开始时间
+     */
+    @TableField("sale_start_time")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    private LocalDate saleStartTime;
+
+    /**
+     * 上架结束时间
+     */
+    @TableField("sale_end_time")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    private LocalDate saleEndTime;
+
+    /**
+     * 状态:1上架 0下架
+     */
+    @TableField("status")
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime updateTime;
+
+    /**
+     * 逻辑删除:1已删除 0未删除
+     */
+    @TableField("deleted")
+    @TableLogic
+    private Integer deleted;
+
+}

+ 80 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductCategory.java

@@ -0,0 +1,80 @@
+package com.ylx.massage.domain;
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 商品分类表 实体类
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("product_category")
+public class ProductCategory implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 父分类ID
+     */
+    @TableField("parent_id")
+    private Long parentId;
+
+    /**
+     * 分类编码
+     */
+    @TableField("code")
+    private String code;
+
+    /**
+     * 分类名称(休闲/日用/娱乐)
+     */
+    @TableField("name")
+    private String name;
+
+    /**
+     * 分类级别
+     */
+    @TableField("level")
+    private Integer level;
+
+    /**
+     * 排序
+     */
+    @TableField("sort")
+    private Integer sort;
+
+    /**
+     * 状态:1启用 0禁用
+     */
+    @TableField("status")
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime updateTime;
+
+}

+ 172 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductOrderInfo.java

@@ -0,0 +1,172 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 商品订单信息
+ */
+@Data
+@TableName("product_order_info")
+public class ProductOrderInfo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 订单编号(雪花算法/业务规则生成)
+     */
+    @TableField("order_no")
+    private String orderNo;
+
+    /**
+     * 用户openId
+     */
+    @TableField("open_id")
+    private String openId;
+
+    /**
+     * 订单状态(0:待付款 1:待发货 2:待收货 3:已收货 4:售后中  11:取消订单)
+     */
+    private Integer orderStatus;
+
+    /**
+     * 支付状态
+     * 0未支付 1已支付 2支付失败 3已退款
+     */
+    private Integer payStatus;
+
+    /**
+     * 支付方式
+     * 1微信 2余额 3:积分支付
+     */
+    private Integer payType;
+
+    /**
+     * 支付时间
+     */
+    private LocalDateTime payTime;
+
+    /**
+     * 支付超时时间(默认下单后15分钟)
+     */
+    private LocalDateTime payExpireTime;
+
+    /**
+     * 商品总金额
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 优惠金额
+     */
+    private BigDecimal discountAmount;
+
+    /**
+     * 运费
+     */
+    private BigDecimal freightAmount;
+
+    /**
+     * 实付金额= total_amount - discount_amount + freight_amount
+     */
+    private BigDecimal payAmount;
+
+    /**
+     * 使用的优惠券ID
+     */
+    private Long couponId;
+
+    /**
+     * 优惠券抵扣金额
+     */
+    private BigDecimal couponAmount;
+
+    /**
+     * 使用积分数量
+     */
+    private Integer pointsUsed;
+
+    /**
+     * 地址ID
+     */
+    private Long addressId;
+
+    /**
+     * 用户姓名
+     */
+    private String userName;
+
+    /**
+     * 用户电话
+     */
+    private String userPhone;
+
+    /**
+     * 用户详细地址
+     */
+    private String userAddress;
+
+    /**
+     * 配送方式
+     * 1快递 2自提
+     */
+    @TableField("delivery_type")
+    private Integer deliveryType;
+
+    /**
+     * 快递公司
+     */
+    private String logisticsCompany;
+
+    /**
+     * 快递单号
+     */
+    private String logisticsNo;
+
+    /**
+     * 发货时间
+     */
+    private LocalDateTime deliveryTime;
+
+    /**
+     * 确认收货时间
+     */
+    private LocalDateTime receiveTime;
+
+    /**
+     * 买家备注
+     */
+    private String buyerRemark;
+
+    /**
+     * 取消原因
+     */
+    private String cancelReason;
+
+    /**
+     * 是否删除 0否 1是
+     */
+    @TableLogic
+    private Integer isDeleted;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}

+ 119 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductOrderItem.java

@@ -0,0 +1,119 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 订单商品明细实体类
+ */
+@Data
+@TableName("product_order_item")
+public class ProductOrderItem implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 订单ID
+     */
+    @TableField
+    private Long orderId;
+
+    /**
+     * 订单编号(冗余,方便查询)
+     */
+    @TableField
+    private String orderNo;
+
+    /**
+     * 商品ID
+     */
+    @TableField
+    private Long productId;
+
+    /**
+     * 商品名称
+     */
+    @TableField
+    private String productName;
+
+    /**
+     * 商品SKU ID
+     */
+    @TableField
+    private Long skuId;
+
+    /**
+     * SKU规格信息快照 JSON,如{"颜色":"红色","尺寸":"XL"}
+     */
+    @TableField
+    private String skuSpec;
+
+    /**
+     * SKU图片
+     */
+    @TableField
+    private String skuImage;
+
+    /**
+     * 下单时单价(分)
+     */
+    @TableField
+    private Integer price;
+
+    /**
+     * 购买数量
+     */
+    @TableField
+    private Integer quantity;
+
+    /**
+     * 总金额(分)= price × quantity
+     */
+    @TableField
+    private BigDecimal totalAmount;
+
+    /**
+     * 优惠金额(分)
+     */
+    @TableField
+    private BigDecimal discountAmount;
+
+    /**
+     * 实付金额(分)
+     */
+    @TableField
+    private BigDecimal actualAmount;
+
+    /**
+     * 退款状态 (0无退款 1退款中 2已退款 3退款失败)
+     */
+    @TableField
+    private Integer refundStatus;
+
+    /**
+     * 退款金额(分)
+     */
+    @TableField
+    private BigDecimal refundAmount;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}

+ 108 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductOrderPayment.java

@@ -0,0 +1,108 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 订单支付记录实体类
+ */
+@Data
+@TableName("product_order_payment")
+public class ProductOrderPayment implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 订单ID
+     */
+    @TableField
+    private String orderId;
+
+    /**
+     * 订单号
+     */
+    @TableField
+    private String orderNo;
+
+    /**
+     * 支付流水号
+     */
+    @TableField(value = "payment_no")
+    private String paymentNo;
+
+    /**
+     * 第三方支付流水号
+     */
+    @TableField(value = "third_payment_no")
+    private String thirdPaymentNo;
+
+    /**
+     * 支付类型 (1-微信支付, 2-余额)
+     */
+    @TableField(value = "pay_type")
+    private Integer payType;
+
+    /**
+     * 支付金额 (单位: 分)
+     */
+    @TableField(value = "pay_amount")
+    private BigDecimal payAmount;
+
+    /**
+     * 支付状态(0待支付 1支付成功 2支付失败
+     */
+    @TableField(value = "pay_status")
+    private String payStatus;
+
+    /**
+     * 支付完成时间
+     */
+    @TableField(value = "pay_time")
+    private LocalDateTime payTime;
+
+    /**
+     * 回调通知时间
+     */
+    @TableField(value = "notify_time")
+    private LocalDateTime notifyTime;
+
+    /**
+     * 回调通知内容
+     */
+    @TableField(value = "notify_content")
+    private String notifyContent;
+
+    /**
+     * 支付过期时间
+     */
+    @TableField(value = "expire_time")
+    private LocalDateTime expireTime;
+
+
+    /**
+     * 备注
+     */
+    @TableField
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}

+ 113 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductSku.java

@@ -0,0 +1,113 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 商品SKU表 实体类
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("product_sku")
+public class ProductSku implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 商品ID
+     */
+    @TableField("product_id")
+    private Long productId;
+
+    /**
+     * SKU编号
+     */
+    @TableField("sku_no")
+    private String skuNo;
+
+    /**
+     * 规格组合(如:白色,加粗,大号)
+     */
+    @TableField("spec_combo")
+    private String specCombo;
+
+    /**
+     * 规格值ID组合(如:1_3_5)
+     */
+    @TableField("spec_value_ids")
+    private String specValueIds;
+
+    /**
+     * SKU图片URL
+     */
+    @TableField("image")
+    private String image;
+
+    /**
+     * 现金价格
+     */
+    @TableField("price_money")
+    private BigDecimal priceMoney;
+
+    /**
+     * 积分价格
+     */
+    @TableField("price_point")
+    private Integer pricePoint;
+
+    /**
+     * 原价
+     */
+    @TableField("origin_price")
+    private BigDecimal originPrice;
+
+    /**
+     * 库存
+     */
+    @TableField("stock")
+    private Integer stock;
+
+    /**
+     * 已售
+     */
+    @TableField("sales")
+    private Integer sales;
+
+    /**
+     * 状态:1启用 0禁用
+     */
+    @TableField("status")
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime updateTime;
+
+}

+ 60 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductSpec.java

@@ -0,0 +1,60 @@
+package com.ylx.massage.domain;
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+/**
+ * 商品规格表 实体类
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("product_spec")
+public class ProductSpec implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 商品ID
+     */
+    @TableField("product_id")
+    private Long productId;
+
+    /**
+     * 商品编号
+     */
+    @TableField("product_no")
+    private String productNo;
+
+    /**
+     * 规格名(颜色/型号/尺码)
+     */
+    @TableField("spec_name")
+    private String specName;
+
+    /**
+     * 排序
+     */
+    @TableField("sort")
+    private Integer sort;
+
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime createTime;
+
+}

+ 69 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ProductSpecValue.java

@@ -0,0 +1,69 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 商品规格值表 实体类
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("product_spec_value")
+public class ProductSpecValue implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 规格名ID
+     */
+    @TableField("spec_id")
+    private Long specId;
+
+    /**
+     * 商品ID(冗余,便于查询)
+     */
+    @TableField("product_id")
+    private Long productId;
+
+    /**
+     * 商品编号
+     */
+    @TableField("product_no")
+    private String productNo;
+
+    /**
+     * 规格值(白色/红色/加粗)
+     */
+    @TableField("spec_value")
+    private String specValue;
+
+    /**
+     * 排序
+     */
+    @TableField("sort")
+    private Integer sort;
+
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime createTime;
+
+}

+ 4 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/domain/RefundVoucher.java

@@ -27,7 +27,10 @@ public class RefundVoucher extends Model<RefundVoucher> {
     private String orderNo;
     //退款原因
     private String reason;
-    //退款状态 0待退款 1已退款
+
+    /**
+     * 退款状态 0待退款 1已退款
+     */
     private Integer reStatus;
     //是否删除0否1是
     private Integer isDelete;

+ 2 - 2
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TAddress.java

@@ -63,9 +63,9 @@ public class TAddress extends Model<TAddress> {
     private Integer userType;
 
     /**
-     * 地址类型 1:默认地址 2:虚拟地址
+     * 地址类型 0:普通地址 1:默认地址 2:虚拟地址
      */
-    @ApiModelProperty("地址类型 1:默认地址 2:虚拟地址")
+    @ApiModelProperty("地址类型 0:普通地址 1:默认地址 2:虚拟地址")
     private Integer type;
 
     /**

+ 46 - 93
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TConsumptionLog.java

@@ -12,7 +12,7 @@ import java.math.BigDecimal;
 import java.util.Date;
 
 /**
- * 个人钱包记录表(TConsumptionLog)表实体类
+ * 个人消费记录表(TConsumptionLog)表实体类
  *
  * @author makejava
  * @since 2024-04-25 16:50:31
@@ -20,37 +20,42 @@ import java.util.Date;
 
 @SuppressWarnings("serial")
 @Data
-@ApiModel(value = "TConsumptionLog", description = "余额记录")
+@ApiModel(value = "TConsumptionLog", description = "个人消费记录")
 public class TConsumptionLog extends Model<TConsumptionLog> {
-    //id
+
+    /**
+     * 主键ID
+     */
     @ApiModelProperty("id")
     private String id;
-    //金额
+
+    /**
+     * 金额
+     */
     @ApiModelProperty("金额")
     private BigDecimal amount;
-    //业务类型 1充值,2余额支付,3技师收益,4技师提现
 
     /**
-     * 业务类型 1充值,2余额支付,3技师收益,4技师提现
+     * 业务类型 1充值,2余额支付,3技师收益,4技师提现 5:微信支付 6分销收益
      */
-    @ApiModelProperty("业务类型 1充值,2余额支付,3技师收益,4技师提现")
+    @ApiModelProperty("业务类型 1充值,2余额支付,3技师收益,4技师提现 5:微信支付 6分销收益")
     private Integer billType;
-    //业务单号
+
+    /**
+     * 业务单号
+     */
     @ApiModelProperty("业务单号")
     private String billNo;
-    //openId
+
+    /**
+     * openId
+     */
     @ApiModelProperty("openId")
     private String openId;
 
-    //部门id
-    @ApiModelProperty("部门id")
-    private String deptId;
-
-    //deptName
-    @ApiModelProperty("deptName")
-    private String deptName;
-
-    //是否可提0否1是
+    /**
+     * 是否可提0否1是
+     */
     @ApiModelProperty("是否可提0否1是")
     private Integer isGet;
 
@@ -60,90 +65,38 @@ public class TConsumptionLog extends Model<TConsumptionLog> {
     @ApiModelProperty("备注")
     private String note;
 
-    //系统创建时间
+    /**
+     * 部门id
+     */
+    @ApiModelProperty("部门id")
+    private String deptId;
+
+    /**
+     * 部门名称
+     */
+    @ApiModelProperty("部门名称")
+    private String deptName;
+
+    /**
+     * 系统创建时间
+     */
     @ApiModelProperty("系统创建时间")
     private Date createTime;
-    //系统修改时间
+
+    /**
+     * 系统修改时间
+     */
     @ApiModelProperty("系统修改时间")
     private Date updateTime;
-    //是否删除0否1是
+
+    /**
+     * 是否删除0否1是
+     */
     @TableLogic
     @ApiModelProperty("是否删除0否1是")
     private Integer isDelete;
 
 
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public BigDecimal getAmount() {
-        return amount;
-    }
-
-    public void setAmount(BigDecimal amount) {
-        this.amount = amount;
-    }
-
-    public Integer getBillType() {
-        return billType;
-    }
-
-    public void setBillType(Integer billType) {
-        this.billType = billType;
-    }
-
-    public String getBillNo() {
-        return billNo;
-    }
-
-    public void setBillNo(String billNo) {
-        this.billNo = billNo;
-    }
-
-    public String getOpenId() {
-        return openId;
-    }
-
-    public void setOpenId(String openId) {
-        this.openId = openId;
-    }
-
-    public String getNote() {
-        return note;
-    }
-
-    public void setNote(String note) {
-        this.note = note;
-    }
-
-    public Date getCreateTime() {
-        return createTime;
-    }
-
-    public void setCreateTime(Date createTime) {
-        this.createTime = createTime;
-    }
-
-    public Date getUpdateTime() {
-        return updateTime;
-    }
-
-    public void setUpdateTime(Date updateTime) {
-        this.updateTime = updateTime;
-    }
-
-    public Integer getIsDelete() {
-        return isDelete;
-    }
-
-    public void setIsDelete(Integer isDelete) {
-        this.isDelete = isDelete;
-    }
-
     /**
      * 获取主键值
      *

+ 30 - 8
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TJs.java

@@ -256,7 +256,7 @@ public class TJs implements Serializable {
      * 0-真实技师(真实存在的技师)
      * 1-虚拟技师(用于模拟服务场景)
      */
-     @TableField("tech_type")
+    @TableField("tech_type")
     @ApiModelProperty("技师类型 0-真实技师 1-虚拟技师")
     private Integer techType;
 
@@ -437,7 +437,7 @@ public class TJs implements Serializable {
      * 用于存储技师的录音文件路径
      */
     @TableField("recording")
-     @ApiModelProperty("录音")
+    @ApiModelProperty("录音")
     private String recording;
 
     /**
@@ -448,21 +448,27 @@ public class TJs implements Serializable {
     @ApiModelProperty("录像")
     private String videoRecording;
 
-     /**
+    /**
      * 承诺书
      * 用于存储技师的服务承诺描述
      */
-     @TableField("commitment")
-     @ApiModelProperty("承诺书")
+    @TableField("commitment")
+    @ApiModelProperty("承诺书")
     private String commitment;
 
-     /**
+    /**
      * 虚拟技师数量
      */
-     @TableField("virtual_tech_number")
-     @ApiModelProperty("虚拟技师数量")
+    @TableField("virtual_tech_number")
+    @ApiModelProperty("虚拟技师数量")
     private Integer virtualTechNumber;
 
+    @ApiModelProperty("可服务三级地区code,逗号分隔")
+    private String serviceAreaCodes;
+
+    @ApiModelProperty("前端选中的地区完整JSON")
+    private String serviceAreaJson;
+
     public String getcVideo() {
         return cVideo;
     }
@@ -656,4 +662,20 @@ public class TJs implements Serializable {
         this.number = number;
     }
 
+
+    public String getServiceAreaCodes() {
+        return serviceAreaCodes;
+    }
+
+    public void setServiceAreaCodes(String serviceAreaCodes) {
+        this.serviceAreaCodes = serviceAreaCodes;
+    }
+
+    public String getServiceAreaJson() {
+        return serviceAreaJson;
+    }
+
+    public void setServiceAreaJson(String serviceAreaJson) {
+        this.serviceAreaJson = serviceAreaJson;
+    }
 }

+ 14 - 43
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TLbt.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Getter;
@@ -34,6 +35,7 @@ public class TLbt implements Serializable {
      */
     @TableId("c_id")
     @ApiModelProperty("cId")
+    @JsonProperty("cId")
     private String cId;
 
     /**
@@ -41,6 +43,7 @@ public class TLbt implements Serializable {
      */
     @TableField("c_img_url")
     @ApiModelProperty("图片地址")
+    @JsonProperty("cImgUrl")
     private String cImgUrl;
 
     /**
@@ -48,6 +51,7 @@ public class TLbt implements Serializable {
      */
     @TableField("c_describe")
     @ApiModelProperty("描述")
+    @JsonProperty("cDescribe")
     private String cDescribe;
 
     /**
@@ -55,57 +59,24 @@ public class TLbt implements Serializable {
      */
     @TableField("c_content")
     @ApiModelProperty("文本内容")
+    @JsonProperty("cContent")
     private String cContent;
 
-    @ApiModelProperty("是否删除0否1是")
-    @TableLogic
-    private Integer isDelete;
-
     /**
      * 序号
      */
     @TableField("c_sort")
     @ApiModelProperty("序号")
+    @JsonProperty("cSort")
     private Integer cSort;
 
-    public String getcId() {
-        return cId;
-    }
-
-    public void setcId(String cId) {
-        this.cId = cId;
-    }
-
-    public String getcImgUrl() {
-        return cImgUrl;
-    }
-
-    public void setcImgUrl(String cImgUrl) {
-        this.cImgUrl = cImgUrl;
-    }
-
-
-    public String getcDescribe() {
-        return cDescribe;
-    }
-
-    public void setcDescribe(String cDescribe) {
-        this.cDescribe = cDescribe;
-    }
-
-    public String getcContent() {
-        return cContent;
-    }
-
-    public void setcContent(String cContent) {
-        this.cContent = cContent;
-    }
-
-    public Integer getcSort() {
-        return cSort;
-    }
+    /**
+     * 是否删除 0否1是
+     */
+    @ApiModelProperty("是否删除0否1是")
+    @TableLogic
+    @TableField("is_delete")
+    @JsonProperty("isDelete")
+    private Integer isDelete;
 
-    public void setcSort(Integer cSort) {
-        this.cSort = cSort;
-    }
 }

+ 0 - 3
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TOrder.java

@@ -114,8 +114,6 @@ public class TOrder implements Serializable {
     @ApiModelProperty("老技师ID")
     private String oldJsId;
 
-
-
     /**
      * 超时原因
      * 订单超时的原因说明
@@ -303,7 +301,6 @@ public class TOrder implements Serializable {
 
     /**
      * 领取优惠券ID
-     * 使用的优惠券记录ID
      */
     @TableField("coupon_receive_id")
     @ApiModelProperty("领取优惠券ID")

+ 7 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TWxUser.java

@@ -48,6 +48,7 @@ public class TWxUser implements Serializable {
     @TableField("role")
     @ApiModelProperty("角色:0普通,1技师")
     private Integer role;
+
     /**
      * 微信小程序登录会话密钥
      */
@@ -159,6 +160,12 @@ public class TWxUser implements Serializable {
     @TableLogic
     private Integer isDelete;
 
+    @ApiModelProperty("是否绑定一账通 0否 1是")
+    private Integer isBind;
+
+    @ApiModelProperty("本地生活用户id")
+    private String localLiveUserId;
+
     public String getcOpenid() {
         return cOpenid;
     }

+ 125 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/UserCouponRelation.java

@@ -0,0 +1,125 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("user_coupon_relation")
+public class UserCouponRelation implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** id */
+    @TableId(type = IdType.ASSIGN_ID)
+    private String id;
+
+    /** 用户openId */
+    private String openId;
+
+    /** 优惠券id */
+    private String couponId;
+
+    /** 优惠券名称 */
+    private String couponName;
+
+    /** 券图URL */
+    private String imageUrl;
+
+    /** 优惠券类型 1-兑换券 2-折扣券 3-满减券 */
+    private Integer couponType;
+
+    /** 优惠券状态 0:待使用,1:使用中 2:已使用 3:已失效 */
+    private Integer couponStatus;
+
+    /** 使用有限期类型 1.用户领取后生效 2.三方用户赠送时间起 3.固定时间段 4.长期可用 */
+    private Integer validityType;
+
+    /** 开始时间 */
+    private LocalDate startTime;
+
+    /** 结束时间 */
+    private LocalDate endTime;
+
+    /** 天数 */
+    private Integer dayNum;
+
+    /** 领取时间 */
+    private LocalDateTime receiveTime;
+
+    /** 订单号 */
+    private String orderNo;
+
+    /** 市场售价 */
+    private BigDecimal marketPrice;
+
+    /** 失效日期 */
+    private LocalDateTime expiringDate;
+
+    /** 失效原因 */
+    private String rejectReason;
+
+    /** 补充说明 */
+    private String additionalNote;
+
+    /** 券来源 0:券中心 1:专属好券 2:免费体验 3:生日好礼 4:每日好礼 */
+    private Integer ticketSource;
+
+    /** 券中心分享用户id */
+    private String centerShareUserId;
+
+    /** 关联类型 1-商品 2-商品分类 */
+    private String relatedType;
+
+    /** 关联ID(商品ID或分类ID) */
+    private String relatedId;
+
+    /** 关联名称 */
+    private String relatedName;
+
+    /** 门槛金额(分): 满X元生效,适用于满减/折扣 */
+    private BigDecimal ruleMinSpendAmount;
+
+    /** 折扣比: 如0.80表示8折,仅折扣券使用 */
+    private BigDecimal ruleDiscountRate;
+
+    /** 折扣封顶金额(分): 折扣最高优惠 */
+    private BigDecimal ruleDiscountCapAmount;
+
+    /** 满减金额(分): 仅满减券使用 */
+    private BigDecimal ruleReductionAmount;
+
+    /** 描述 */
+    private String description;
+
+    /** 券中心唯一ID */
+    private String ticketCenterId;
+
+    /** 所在地区码 */
+    private String country;
+
+    /** 所在地区全部码 */
+    private String countryAll;
+
+    /** 所在地区 */
+    private String locality;
+
+    /** 商户id */
+    private String merchantId;
+
+    /** 商户名称 */
+    private String storeName;
+
+    /** 创建时间 */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+
+    /** 修改时间 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}

+ 35 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderDTO.java

@@ -0,0 +1,35 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel(description = "售后添加请求")
+public class AfterSaleOrderDTO {
+
+    @NotNull(message = "订单ID不能为空")
+    @ApiModelProperty(value = "订单ID", required = true)
+    private Long orderId;
+
+    @NotBlank(message = "订单编号不能为空")
+    @ApiModelProperty(value = "订单编号", required = true)
+    private String orderNo;
+
+    @NotNull(message = "申请原因不能为空")
+    @ApiModelProperty(value = "申请原因", required = true)
+    private String applyReason;
+
+    @ApiModelProperty(value = "售后类型 (1退货 2换货 3未收到货)")
+    private Integer afterSaleType;
+
+    @ApiModelProperty(value = "问题描述")
+    private String applyDesc;
+
+    @ApiModelProperty(value = "凭证图片URL,多个逗号分隔")
+    private String applyImages;
+
+}

+ 22 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderFeeBatchDTO.java

@@ -0,0 +1,22 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+@Data
+@ApiModel(description = "售后订单费用批量添加请求")
+public class AfterSaleOrderFeeBatchDTO {
+
+    @ApiModelProperty("售后单ID")
+    private Long afterSaleOrderId;
+
+    @NotEmpty(message = "费用明细不能为空")
+    @Valid // 开启嵌套校验
+    @ApiModelProperty("费用明细列表")
+    private List<AfterSaleOrderFeeDTO> feeList;
+}

+ 30 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderFeeDTO.java

@@ -0,0 +1,30 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Positive;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("售后费用详情dto")
+public class AfterSaleOrderFeeDTO {
+
+    @ApiModelProperty("费用ID(新增不传,更新传)")
+    private Long id;
+
+    @NotBlank(message = "费用类型不能为空")
+    @ApiModelProperty("费用类型")
+    private String type;
+
+    @NotNull(message = "金额不能为空")
+    @Positive(message = "金额必须大于0")
+    @ApiModelProperty("金额")
+    private BigDecimal fee;
+
+    @ApiModelProperty("描述")
+    private String remark;
+}

+ 23 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/AfterSaleOrderUpdateDTO.java

@@ -0,0 +1,23 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel(description = "售后修改dto")
+public class AfterSaleOrderUpdateDTO {
+
+    @ApiModelProperty(value = "售后单ID", required = true)
+    private Long afterSaleOrderId;
+
+    @NotBlank(message = "物流单号不能为空")
+    @ApiModelProperty(value = "物流单号", required = true)
+    private String logisticsNo;
+
+    @ApiModelProperty(value = "订单ID", required = true)
+    private Long orderId;
+
+}

+ 17 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/CoordinateDTO.java

@@ -0,0 +1,17 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(description = "经纬度查询dto")
+public class CoordinateDTO {
+
+    @ApiModelProperty(value = "经度", required = true)
+    private double longitude;
+
+    @ApiModelProperty(value = "纬度", required = true)
+    private double latitude;
+
+}

+ 16 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/OptionDTO.java

@@ -0,0 +1,16 @@
+package com.ylx.massage.domain.dto;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class OptionDTO extends Page {
+
+    @ApiModelProperty("商品名称")
+    private String title;
+
+    @ApiModelProperty("商品类型:0-按摩商品 1-积分商品")
+    private Integer productType;
+
+}

+ 145 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductCreateDTO.java

@@ -0,0 +1,145 @@
+package com.ylx.massage.domain.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 新增商品请求DTO
+ *
+ * @author ylx
+ * @since 2026-03-26
+ */
+@Data
+@ApiModel("新增商品请求")
+public class ProductCreateDTO {
+
+    /**
+     * 分类ID
+     */
+    @ApiModelProperty(value = "分类ID", required = true)
+    @NotNull(message = "分类ID不能为空")
+    private Long categoryId;
+
+    /**
+     * 商品编号
+     */
+    @ApiModelProperty(value = "商品编号", required = true)
+    @NotBlank(message = "商品编号不能为空")
+    private String productNo;
+
+    /**
+     * 商品名称
+     */
+    @ApiModelProperty(value = "商品名称", required = true)
+    @NotBlank(message = "商品名称不能为空")
+    private String name;
+
+    /**
+     * 商品主图URL
+     */
+    @ApiModelProperty(value = "商品主图URL", required = true)
+    @NotBlank(message = "商品主图不能为空")
+    private String productMainImage;
+
+    /**
+     * 商品图URL列表
+     */
+    @ApiModelProperty("商品图URL列表")
+    private List<String> productImage;
+
+    /**
+     * 商品简述
+     */
+    @ApiModelProperty("商品简述")
+    private String summary;
+
+    /**
+     * 商品详情(富文本HTML)
+     */
+    @ApiModelProperty("商品详情")
+    private String detail;
+
+    /**
+     * 规格列表
+     */
+    @ApiModelProperty("规格列表")
+    @Valid
+    private List<ProductSpecDTO> specList;
+
+    /**
+     * 商品的付款类型(1:积分 2:金额 3:积分+金额)
+     */
+    @ApiModelProperty(value = "付款类型", required = true)
+    private Integer paymentType;
+
+    /**
+     * SKU列表(规格组合明细)
+     */
+    @ApiModelProperty("SKU列表")
+    @Valid
+    private List<ProductSkuDTO> skuList;
+
+    /**
+     * 积分价格
+     */
+    /*@ApiModelProperty("积分价格")
+    private Integer pricePoint;*/
+
+    /**
+     * 现金价格
+     */
+    /*@ApiModelProperty("现金价格")
+    private BigDecimal priceMoney;*/
+
+    /**
+     * 库存(无规格时使用)
+     */
+    /*@ApiModelProperty("库存")
+    private Integer stock;*/
+
+    /**
+     * 基础运费
+     */
+    @ApiModelProperty("基础运费")
+    private BigDecimal freight;
+
+    /**
+     * 承诺发货时间
+     */
+    @ApiModelProperty("承诺发货时间(如48小时)")
+    private String deliveryTime;
+
+    /**
+     * 服务承诺列表
+     * 例如:["假一赔三", "7天无理由退换", "破损包退换"]
+     */
+    @ApiModelProperty("服务承诺列表")
+    private List<String> servicePromise;
+
+    /**
+     * 上架开始时间
+     */
+    @ApiModelProperty("上架开始时间")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate saleStartTime;
+
+    /**
+     * 上架结束时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @ApiModelProperty("上架结束时间")
+    private LocalDate saleEndTime;
+}

+ 33 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderCancelRequest.java

@@ -0,0 +1,33 @@
+package com.ylx.massage.domain.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 商品订单取消请求DTO
+ */
+@Data
+public class ProductOrderCancelRequest implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 订单编号
+     */
+    @NotBlank(message = "订单编号不能为空")
+    private String orderNo;
+
+    /**
+     * 用户openId
+     */
+    @NotBlank(message = "用户openId不能为空")
+    private String openId;
+
+    /**
+     * 取消原因
+     */
+    @NotBlank(message = "取消原因不能为空")
+    private String cancelReason;
+}

+ 114 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderCreateRequest.java

@@ -0,0 +1,114 @@
+package com.ylx.massage.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 商品订单创建请求DTO
+ */
+@Data
+public class ProductOrderCreateRequest implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    private String openId;
+
+    /**
+     * 用户姓名
+     */
+    private String userName;
+
+    /**
+     * 用户电话
+     */
+    private String userPhone;
+
+    /**
+     * 用户地址
+     */
+    private String userAddress;
+
+    /**
+     * 商品ID
+     */
+    private Long productId;
+
+    /**
+     * 商品名称
+     */
+    private String name;
+
+    /**
+     * 商品主图URL
+     */
+    private String productMainImage;
+
+    /**
+     * 选中的SKU ID
+     */
+    private Long skuId;
+
+    /**
+     * SKU列表(规格组合明细)
+     */
+    private List<SpecNameValue> specNameValueList;
+
+    /**
+     * 购买数量
+     */
+    private Integer quantity;
+
+    /**
+     * 支付方式:1微信支付 2账户余额
+     */
+    private Integer payType;
+
+    /**
+     * 运费
+     */
+    private BigDecimal freightAmount;
+
+    /**
+     * 总金额
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 支付金额
+     */
+    private BigDecimal payAmount;
+
+    /**
+     * 使用的积分
+     */
+    private Integer pointsUsed;
+
+    /**
+     * 买家备注信息
+     */
+    private String buyerRemark;
+
+    /**
+     * SKU规格名称值对象
+     */
+    @Data
+    public static class SpecNameValue implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 规格名称
+         */
+        private String specName;
+
+        /**
+         * 规格值
+         */
+        private String specValue;
+    }
+}

+ 22 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderEditLogisticsNo.java

@@ -0,0 +1,22 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("积分订单修改物流单号dto")
+public class ProductOrderEditLogisticsNo {
+
+    @NotNull(message = "订单ID不能为空")
+    @ApiModelProperty(value = "订单ID", required = true)
+    private Long orderId;
+
+    @NotBlank(message = "物流单号不能为空")
+    @ApiModelProperty(value = "物流单号", required = true)
+    private String logisticsNo;
+
+}

+ 29 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/ProductOrderOperateDTO.java

@@ -0,0 +1,29 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("商品订单操作通用DTO")
+public class ProductOrderOperateDTO {
+
+    @NotNull(message = "订单ID不能为空")
+    @ApiModelProperty(value = "订单ID", required = true)
+    private Long orderId;
+
+    @ApiModelProperty("操作备注/原因")
+    private String remark;
+
+    @ApiModelProperty("用户姓名")
+    private String userName;
+
+    @ApiModelProperty("用户电话")
+    private String userPhone;
+
+    @ApiModelProperty("用户详细地址")
+    private String userAddress;
+
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików