94 Commits b9699ef16d ... 64b56ab419

Auteur SHA1 Bericht Datum
  郭子栋 64b56ab419 首页按摩商户全部推荐距离展示优化 5 dagen geleden
  郭子栋 cec24be1ca Merge remote-tracking branch 'origin/dev' into dev 5 dagen geleden
  郭子栋 53d738ab06 首页按摩商户推荐列表距离展示优化 5 dagen geleden
  jinshihui 24a8a4d95b Merge remote-tracking branch 'origin/dev' into dev 5 dagen geleden
  jinshihui c1c1f5954b 修改代码 5 dagen geleden
  郭子栋 3a6e761a0b 首页选择城市优化 5 dagen geleden
  jinshihui 2ae930a4ea 开发查询商户入驻审核列表接口 6 dagen geleden
  wangzhijun e215708edd 提交bookDetail接口,用于“客户端获取去预约项目详情” 6 dagen geleden
  jinwenhai 46fb07c11d 广誉源商户端-登录-我的技能,城市管理 6 dagen geleden
  郭子栋 accccbe4b9 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  郭子栋 4c5c355d6b 首页商户按摩推荐增加根据项目id查询 6 dagen geleden
  jinshihui c10aac7c56 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  jinshihui 8b03126d4f 修改问题 6 dagen geleden
  wangzhijun f0b419b469 提交bookDetail接口,用于“客户端获取去预约项目详情” 6 dagen geleden
  jinshihui f06dc99e19 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  jinshihui 57e02bc330 开发上传合同接口 6 dagen geleden
  jinwenhai ca021dc12c 广誉源商户端-登录-我的技能,城市管理 6 dagen geleden
  jinwenhai 943505df14 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  jinwenhai 55d6f22c51 广誉源商户端-登录-我的技能,城市管理 6 dagen geleden
  郭子栋 c2fe370273 推荐商户补充形象照返回 6 dagen geleden
  郭子栋 b815d651ce 按摩推荐关联ma_project类型修改 6 dagen geleden
  郭子栋 acaf45e372 首页按摩商户推荐列表展示距离优化 6 dagen geleden
  郭子栋 e12ab6efcf 首页推荐商户点击全部商户列表 6 dagen geleden
  郭子栋 e8e90714c2 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  郭子栋 673580145f 首页推荐商户点击全部商户列表 6 dagen geleden
  wangzhijun 6802d8918e 修改订单字段类型 6 dagen geleden
  wangzhijun a9b61888b6 接口备注修改 6 dagen geleden
  jinshihui 53e561b842 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  jinshihui 9c6328d002 优化代码 6 dagen geleden
  wangzhijun 42dea99323 添加接口:getProjectTabList,用于“客户端根据服务类目获取服务项目集合数据” 6 dagen geleden
  jinshihui a2258f96f3 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  jinshihui bf9c8ff91c 优化代码 6 dagen geleden
  wangzhijun e57ca8022c 修改项目的属性 6 dagen geleden
  wangzhijun 2394682847 项目表冗余亮点数据 6 dagen geleden
  jinwenhai 4aab94c668 Merge remote-tracking branch 'origin/dev' into dev 6 dagen geleden
  jinwenhai 08ef79c2b4 广誉源商户端-登录-我的技能 6 dagen geleden
  wangzhijun 85dcbc35b8 项目表调整表结构 6 dagen geleden
  jinwenhai 20768fc1d3 广誉源商户端-登录-我的技能 6 dagen geleden
  jinwenhai 09753d0e56 广誉源商户端-登录-我的技能 6 dagen geleden
  郭子栋 0bcbef8eed 首页推荐项目接口优化 6 dagen geleden
  jinshihui 49c45ca350 修改合并后丢失的代码 6 dagen geleden
  郭子栋 6a56205681 首页推荐项目接口、首页按摩商户推荐优化 6 dagen geleden
  jinshihui 426da2b86b Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  jinshihui c99721be0e 开发商户管理相关的接口 1 week geleden
  wangzhijun 0cc6a4408c 调整订单相关代码 1 week geleden
  郭子栋 8ed69fe588 首页推荐项目接口、首页按摩商户推荐优化 1 week geleden
  wangzhijun 9ab18c76ec 调整订单相关目录结构 1 week geleden
  jinwenhai 877ea56bdd Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  jinwenhai 30b4750402 广誉源-商户端-实体类修改 1 week geleden
  郭子栋 546c45e6b7 商户表报错回滚 1 week geleden
  郭子栋 1062b221de 商户表报错回滚 1 week geleden
  郭子栋 f7804e7a2e 首页按摩推荐商户列表优化 1 week geleden
  郭子栋 f422f16cc4 首页选择城市、首页按摩推荐商户、首页选中城市是否有开通商户、订单状态流转表 1 week geleden
  jinshihui 9057dfbd05 Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  jinshihui 6a7d18d2ea 开发完成考勤配置相关的接口 1 week geleden
  wangzhijun e26f1ce9e3 调整购物卡订单代码 1 week geleden
  jinwenhai b9d56b4346 Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  jinwenhai 84afe33630 广誉源-商户端-实体类修改 1 week geleden
  wangzhijun 42c9e350b0 调整微信支付回调接口 1 week geleden
  wangzhijun 960dbefdbb 添加processGiftCardPayment方法,用于处理礼品卡支付逻辑。 1 week geleden
  jinshihui 4bb4937aa9 Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  jinshihui c3ec57d143 开发完成购物卡管理相关的接口 1 week geleden
  wangzhijun 0c402296d5 调整代码,添加必要的日志输出 1 week geleden
  wangzhijun 8c19a00b55 添加微信支付类型,发起微信下单时设置attach,用于微信支付回调时使用 1 week geleden
  wangzhijun 8d195a0ff8 购物卡、购物订单代码提交 1 week geleden
  wangzhijun deb696def9 微信支付相关代码封装 1 week geleden
  jinshihui 153a69001a 开发项目相关的接口 1 week geleden
  郭子栋 eed0d9986c Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  郭子栋 8436e58ec4 购物金明细查询返回时间格式优化 1 week geleden
  wangzhijun 76723d951a 购物卡、购物卡订单代码提交 1 week geleden
  郭子栋 7099a9aa28 购物金明细增加订单号字段 1 week geleden
  郭子栋 57972df8ec Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  郭子栋 11679b2b0a 购物金、购物金明细提交优化 1 week geleden
  jinshihui 0b94b34c06 Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  jinshihui e2610744f4 优化代码 1 week geleden
  郭子栋 9a3624a8e2 购物金、购物金明细提交 1 week geleden
  wangzhijun b031b1e45b 购物卡、购物卡订单代码提交 1 week geleden
  wangzhijun 40a0f3d602 购物卡、购物卡订单代码提交 1 week geleden
  wangzhijun f9412908f6 代码结构调整 1 week geleden
  wangzhijun 7dc7d93958 初次提交购物卡、购物卡订单相关代码 1 week geleden
  jinshihui 02c6b75d25 Merge remote-tracking branch 'origin/dev' into dev 1 week geleden
  jinshihui 3462df9f08 优化代码 1 week geleden
  wangzhijun c1aeeb39ca 添加CustomerAuth、MerchantAuth 1 week geleden
  wangzhijun 5a478aa702 优化draw接口方法 1 week geleden
  jinshihui 7df76900ad 修改了问题 2 weken geleden
  wangzhijun 081669a602 修改线上数据库为“guangyuyuan” 2 weken geleden
  wangzhijun ab31ddee9b “分页查询轮播图数据”接口免token 2 weken geleden
  wangzhijun 6d6d659256 添加“跳转地址”、“跳转类型”和“显示状态”三个字段 2 weken geleden
  wangzhijun ec8eb677d5 完成相关代码提交 2 weken geleden
  wangzhijun f5cecd9adc 项目相关代码提交 2 weken geleden
  jinshihui a3e2c1f8ab 开发服务类目-首页(h5)接口 2 weken geleden
  jinshihui e4d912914e 开发完成服务类目相关的接口 2 weken geleden
  jinshihui 549d9e8b2c 修改代码 2 weken geleden
  jinshihui 15ed6a2112 修改了数据库配置信息 2 weken geleden
100 gewijzigde bestanden met toevoegingen van 5758 en 1686 verwijderingen
  1. 1 1
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ApiUserPointController.java
  2. 104 102
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/CancelOrderApplicationController.java
  3. 1 1
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/HomeController.java
  4. 456 24
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/MaTechnicianController.java
  5. 149 292
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/PayController.java
  6. 1 2
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TCommentController.java
  7. 4 24
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TFinancialIncomeController.java
  8. 23 23
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TOrderController.java
  9. 0 1
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TTxRecordController.java
  10. 1 1
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WeChatController.java
  11. 77 2
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WeSqController.java
  12. 3 0
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WxController.java
  13. 2 6
      nightFragrance-admin/src/main/java/com/ylx/web/controller/point/UserPointController.java
  14. 11 19
      nightFragrance-admin/src/main/java/com/ylx/web/controller/system/SysDictDataController.java
  15. 13 23
      nightFragrance-admin/src/main/java/com/ylx/web/controller/system/SysDictTypeController.java
  16. 25 9
      nightFragrance-admin/src/main/resources/application-dev.yml
  17. 22 2
      nightFragrance-admin/src/main/resources/application-pro.yml
  18. 6 0
      nightFragrance-common/pom.xml
  19. 20 0
      nightFragrance-common/src/main/java/com/ylx/common/component/CustomerAuth.java
  20. 20 0
      nightFragrance-common/src/main/java/com/ylx/common/component/MerchantAuth.java
  21. 82 0
      nightFragrance-common/src/main/java/com/ylx/common/config/WxPayBeanConfig.java
  22. 1 1
      nightFragrance-common/src/main/java/com/ylx/common/core/domain/model/aliyun/SendSmsEnum.java
  23. 18 0
      nightFragrance-common/src/main/java/com/ylx/common/weixinPay/enums/WxPayTypeEnum.java
  24. 90 0
      nightFragrance-common/src/main/java/com/ylx/common/weixinPay/service/WxPayV3Service.java
  25. 1 1
      nightFragrance-framework/src/main/java/com/ylx/framework/config/SecurityConfig.java
  26. 2 2
      nightFragrance-framework/src/main/java/com/ylx/framework/web/service/WxTokenService.java
  27. 4 1
      nightFragrance-massage/pom.xml
  28. 115 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/controller/AttendanceConfigController.java
  29. 110 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/AttendanceDeductionRule.java
  30. 103 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/AttendanceRule.java
  31. 38 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/dto/AttendanceDeductionRuleDTO.java
  32. 49 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/dto/AttendanceRuleAddDTO.java
  33. 32 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/vo/AttendanceDeductionRuleVO.java
  34. 46 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/vo/AttendanceRuleDetailVO.java
  35. 7 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/mapper/AttendanceDeductionRuleMapper.java
  36. 7 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/mapper/AttendanceRuleMapper.java
  37. 17 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/service/AttendanceConfigService.java
  38. 220 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/service/impl/AttendanceConfigServiceImpl.java
  39. 49 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/controller/GiftCardController.java
  40. 177 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/controller/GiftCardManageController.java
  41. 13 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/controller/GiftCardOrderController.java
  42. 90 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/GiftCard.java
  43. 105 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/GiftCardOrder.java
  44. 29 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageQueryDTO.java
  45. 93 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageSaveDTO.java
  46. 19 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageUpdateDTO.java
  47. 25 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardPublishStatusDTO.java
  48. 25 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardPurchaseDTO.java
  49. 43 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardDetailVO.java
  50. 16 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManageDetailVO.java
  51. 42 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManageExportVO.java
  52. 54 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManagePageVO.java
  53. 53 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardVO.java
  54. 21 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/enums/GiftCardOrderStatusEnum.java
  55. 16 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/mapper/GiftCardMapper.java
  56. 8 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/mapper/GiftCardOrderMapper.java
  57. 17 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/service/IGiftCardOrderService.java
  58. 40 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/service/IGiftCardService.java
  59. 208 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardOrderServiceImpl.java
  60. 453 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardServiceImpl.java
  61. 26 0
      nightFragrance-massage/src/main/java/com/ylx/massage/constant/CommonConstant.java
  62. 87 0
      nightFragrance-massage/src/main/java/com/ylx/massage/controller/CityOperationApplicationController.java
  63. 58 0
      nightFragrance-massage/src/main/java/com/ylx/massage/controller/MassageRecommendController.java
  64. 5 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/AfterSaleOrder.java
  65. 88 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/CityOperationApplication.java
  66. 69 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ContractRecord.java
  67. 89 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/ContractRecords.java
  68. 104 26
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaProject.java
  69. 25 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTeProject.java
  70. 238 197
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTechnician.java
  71. 6 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TAddress.java
  72. 20 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TLbt.java
  73. 0 863
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TOrder.java
  74. 9 2
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TWxUser.java
  75. 26 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaProjectSaveDto.java
  76. 31 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaProjectUpdateDto.java
  77. 61 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaTechnicianAuditQueryDTO.java
  78. 63 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaTechnicianMerchantAddDTO.java
  79. 73 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaTechnicianMerchantQueryDTO.java
  80. 55 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MassageAllMerchantsDto.java
  81. 38 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MassageMerchantRecommendDto.java
  82. 41 41
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/CancelOrderApplicationDetailVo.java
  83. 19 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaProjectGetVo.java
  84. 127 16
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianAppAddVo.java
  85. 89 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianAuditListVO.java
  86. 97 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianMerchantDetailVO.java
  87. 138 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianMerchantListVO.java
  88. 55 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MassageAllMerchantsVo.java
  89. 39 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MassageProjectRecommendVo.java
  90. 25 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MassageRecommendVo.java
  91. 59 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MerchantVo.java
  92. 198 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/Result.java
  93. 22 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/ThirdPartyLoginsVo.java
  94. 16 0
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/CityOperationApplicationMapper.java
  95. 8 0
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/ContractRecordMapper.java
  96. 23 2
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaProjectMapper.java
  97. 34 0
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTeProjectMapper.java
  98. 97 2
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTechnicianMapper.java
  99. 5 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/AreaService.java
  100. 18 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/CityOperationApplicationService.java

+ 1 - 1
nightFragrance-admin/src/main/java/com/ylx/web/controller/api/ApiUserPointController.java → nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/ApiUserPointController.java

@@ -1,4 +1,4 @@
-package com.ylx.web.controller.api;
+package com.ylx.web.controller.massage;
 
 import com.ylx.common.core.domain.R;
 import com.ylx.point.domain.dto.ApiAddPointsDTO;

+ 104 - 102
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/CancelOrderApplicationController.java

@@ -11,12 +11,12 @@ import com.ylx.common.utils.SecurityUtils;
 import com.ylx.common.utils.StringUtils;
 import com.ylx.massage.domain.CancelOrderApplication;
 import com.ylx.massage.domain.TJs;
-import com.ylx.massage.domain.TOrder;
+import com.ylx.order.domain.TOrder;
 import com.ylx.massage.domain.vo.CancelOrderApplicationDetailVo;
 import com.ylx.massage.enums.OrderStatusEnum;
 import com.ylx.massage.service.CancelOrderApplicationService;
 import com.ylx.massage.service.TJsService;
-import com.ylx.massage.service.TOrderService;
+import com.ylx.order.service.TOrderService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
@@ -111,64 +111,65 @@ public class CancelOrderApplicationController extends BaseController {
     @ApiOperation("根据主键ID查询订单详情")
     @GetMapping(value = "/getByOrderId")
     public R<CancelOrderApplicationDetailVo> getByOrderId(@RequestParam("id") String id) {
-        try {
-            if (StringUtils.isBlank(id)) {
-                return R.fail("ID不能为空");
-            }
-            //通过主键ID查询退单申请信息
-            CancelOrderApplication application = cancelOrderApplicationService.getById(id);
-            if (application == null) {
-                return R.fail("退单申请不存在");
-            }
-            String orderId = application.getOrderId();
-            TOrder order = orderService.getById(orderId);
-            if (order == null) {
-                return R.fail("订单不存在");
-            }
-            CancelOrderApplicationDetailVo vo = new CancelOrderApplicationDetailVo();
-            BeanUtils.copyProperties(order, vo);
-            vo.setId(id);
-            Integer status = vo.getnStatus();
-            vo.setStatusName(OrderStatusEnum.getDescByCode(status));
-
-            //获取技师ID
-            String techId = order.getcJsId();
-            //根据技师ID查询技师信息
-            TJs tech = jsService.getById(techId);
-            vo.setJsName(tech.getcName());
-            vo.setCNickName(tech.getcNickName());
-            vo.setJsPhone(tech.getcPhone());
-
-            // 设置服务时长(分钟)
-            vo.setServiceDuration(application.getServiceDuration());
-            JSONArray objects = order.getcGoods();
-            // 遍历商品列表,累加项目金额
-            BigDecimal projectPrice = BigDecimal.ZERO;
-            StringBuffer projectNameBuffer = new StringBuffer();
-            //项目时长
-            BigDecimal projectDuration = new BigDecimal(0);
-            for (int i = 0; i < objects.size(); i++) {
-                JSONObject object = objects.getJSONObject(i);
-                BigDecimal price = object.getBigDecimal("dPrice");
-                projectPrice = projectPrice.add(price);
-                projectNameBuffer.append(object.getString("cTitle")).append(" ");
-                projectDuration = projectDuration.add(object.getBigDecimal("nMinute"));
-            }
-            // 设置退单原因
-            vo.setCancelOrderReason(application.getCancelOrderReason());
-            // 设置项目名称
-            vo.setProjectName(projectNameBuffer.toString().trim());
-            // 设置项目时长
-            vo.setProjectDuration(projectDuration.intValue());
-            // 设置项目金额
-            vo.setProjectPrice(projectPrice);
-            // 设置退款金额
-            vo.setRefundAmount(application.getRefundAmount());
-            return R.ok(vo);
-        } catch (Exception e) {
-            e.printStackTrace();
-            throw new RuntimeException(e);
-        }
+//        try {
+//            if (StringUtils.isBlank(id)) {
+//                return R.fail("ID不能为空");
+//            }
+//            //通过主键ID查询退单申请信息
+//            CancelOrderApplication application = cancelOrderApplicationService.getById(id);
+//            if (application == null) {
+//                return R.fail("退单申请不存在");
+//            }
+//            String orderId = application.getOrderId();
+//            TOrder order = orderService.getById(orderId);
+//            if (order == null) {
+//                return R.fail("订单不存在");
+//            }
+//            CancelOrderApplicationDetailVo vo = new CancelOrderApplicationDetailVo();
+//            BeanUtils.copyProperties(order, vo);
+//            vo.setId(id);
+//            Integer status = vo.getnStatus();
+//            vo.setStatusName(OrderStatusEnum.getDescByCode(status));
+//
+//            //获取技师ID
+//            String techId = order.getcJsId();
+//            //根据技师ID查询技师信息
+//            TJs tech = jsService.getById(techId);
+//            vo.setJsName(tech.getcName());
+//            vo.setCNickName(tech.getcNickName());
+//            vo.setJsPhone(tech.getcPhone());
+//
+//            // 设置服务时长(分钟)
+//            vo.setServiceDuration(application.getServiceDuration());
+//            JSONArray objects = order.getcGoods();
+//            // 遍历商品列表,累加项目金额
+//            BigDecimal projectPrice = BigDecimal.ZERO;
+//            StringBuffer projectNameBuffer = new StringBuffer();
+//            //项目时长
+//            BigDecimal projectDuration = new BigDecimal(0);
+//            for (int i = 0; i < objects.size(); i++) {
+//                JSONObject object = objects.getJSONObject(i);
+//                BigDecimal price = object.getBigDecimal("dPrice");
+//                projectPrice = projectPrice.add(price);
+//                projectNameBuffer.append(object.getString("cTitle")).append(" ");
+//                projectDuration = projectDuration.add(object.getBigDecimal("nMinute"));
+//            }
+//            // 设置退单原因
+//            vo.setCancelOrderReason(application.getCancelOrderReason());
+//            // 设置项目名称
+//            vo.setProjectName(projectNameBuffer.toString().trim());
+//            // 设置项目时长
+//            vo.setProjectDuration(projectDuration.intValue());
+//            // 设置项目金额
+//            vo.setProjectPrice(projectPrice);
+//            // 设置退款金额
+//            vo.setRefundAmount(application.getRefundAmount());
+//            return R.ok(vo);
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//            throw new RuntimeException(e);
+//        }
+        return null;
     }
 
     /**
@@ -180,48 +181,49 @@ public class CancelOrderApplicationController extends BaseController {
     @ApiOperation("审核退单申请")
     @PostMapping("/audit")
     public R audit(@RequestBody CancelOrderApplication cancelOrderApplication) {
-        try {
-            if (StringUtils.isBlank(cancelOrderApplication.getId())) {
-                return R.fail("退单申请ID不能为空");
-            }
-            // 检查申请是否存在
-            CancelOrderApplication application = cancelOrderApplicationService.getById(cancelOrderApplication.getId());
-            if (application == null) {
-                return R.fail("退单申请不存在");
-            }
-            //检查当前审核状态是否为待审核
-            if (application.getAuditStatus() != 0) {
-                return R.fail("退单申请当前状态不是待审核");
-            }
-
-            // 审核退单申请
-            application.setRefundAmount(cancelOrderApplication.getRefundAmount());
-            application.setAuditRemark(cancelOrderApplication.getAuditRemark());
-            application.setOrderStatus(8);
-            application.setAuditStatus(cancelOrderApplication.getAuditStatus());
-            // 审核通过,更新订单状态为退单审核通过
-            if (application.getAuditStatus() == 1) {
-                // 更新订单状态
-                TOrder order = new TOrder();
-                order.setcId(application.getOrderId());
-                order.setnStatus(8);
-                orderService.updateById(order);
-            }
-            // 审核时间
-            application.setAuditTime(LocalDateTime.now());
-            // 审核人ID
-            application.setAuditUserId(SecurityUtils.getUserId().toString());
-            // 审核人姓名
-            application.setAuditUserName(SecurityUtils.getUsername());
-            boolean b = cancelOrderApplicationService.updateById(application);
-            if (!b) {
-                return R.fail("审核退单申请失败");
-            }
-            return R.ok("审核退单申请成功");
-        } catch (Exception e) {
-            log.error("审核退单申请失败,申请ID:{}", cancelOrderApplication.getId(), e);
-            return R.fail("审核退单申请失败:" + e.getMessage());
-        }
+//        try {
+//            if (StringUtils.isBlank(cancelOrderApplication.getId())) {
+//                return R.fail("退单申请ID不能为空");
+//            }
+//            // 检查申请是否存在
+//            CancelOrderApplication application = cancelOrderApplicationService.getById(cancelOrderApplication.getId());
+//            if (application == null) {
+//                return R.fail("退单申请不存在");
+//            }
+//            //检查当前审核状态是否为待审核
+//            if (application.getAuditStatus() != 0) {
+//                return R.fail("退单申请当前状态不是待审核");
+//            }
+//
+//            // 审核退单申请
+//            application.setRefundAmount(cancelOrderApplication.getRefundAmount());
+//            application.setAuditRemark(cancelOrderApplication.getAuditRemark());
+//            application.setOrderStatus(8);
+//            application.setAuditStatus(cancelOrderApplication.getAuditStatus());
+//            // 审核通过,更新订单状态为退单审核通过
+//            if (application.getAuditStatus() == 1) {
+//                // 更新订单状态
+//                TOrder order = new TOrder();
+//                order.setcId(application.getOrderId());
+//                order.setnStatus(8);
+//                orderService.updateById(order);
+//            }
+//            // 审核时间
+//            application.setAuditTime(LocalDateTime.now());
+//            // 审核人ID
+//            application.setAuditUserId(SecurityUtils.getUserId().toString());
+//            // 审核人姓名
+//            application.setAuditUserName(SecurityUtils.getUsername());
+//            boolean b = cancelOrderApplicationService.updateById(application);
+//            if (!b) {
+//                return R.fail("审核退单申请失败");
+//            }
+//            return R.ok("审核退单申请成功");
+//        } catch (Exception e) {
+//            log.error("审核退单申请失败,申请ID:{}", cancelOrderApplication.getId(), e);
+//            return R.fail("审核退单申请失败:" + e.getMessage());
+//        }
+        return null;
     }
 
 

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

@@ -8,7 +8,7 @@ import com.ylx.massage.domain.TJsDay;
 import com.ylx.massage.domain.vo.HomeBlock;
 import com.ylx.massage.domain.vo.HomeBlocks;
 import com.ylx.massage.mapper.*;
-import com.ylx.massage.service.TOrderService;
+import com.ylx.order.service.TOrderService;
 import com.ylx.massage.utils.DateTimeUtils;
 import com.ylx.massage.utils.WeChatUtil;
 import com.ylx.system.service.ISysOperLogService;

+ 456 - 24
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/MaTechnicianController.java

@@ -1,21 +1,44 @@
 package com.ylx.web.controller.massage;
 
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import com.ylx.massage.domain.vo.MaTechnicianAppAddVo;
+import cn.hutool.json.JSONObject;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.core.domain.model.LoginUser;
+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.utils.StringUtils;
+import com.ylx.massage.domain.MaProject;
+import com.ylx.massage.domain.dto.MaProjectSaveDto;
+import com.ylx.massage.domain.dto.MaProjectUpdateDto;
+import com.ylx.massage.domain.dto.MaTechnicianAuditQueryDTO;
+import com.ylx.massage.domain.dto.MaTechnicianMerchantAddDTO;
+import com.ylx.massage.domain.dto.MaTechnicianMerchantQueryDTO;
+import com.ylx.massage.domain.vo.*;
+import com.ylx.massage.service.IMaProjectService;
+import com.ylx.project.domain.Project;
+import com.ylx.servicecategory.domain.ServiceCategory;
+import com.ylx.servicecategory.service.ServiceCategoryService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.bind.annotation.*;
 import com.ylx.common.annotation.Log;
 import com.ylx.common.core.controller.BaseController;
 import com.ylx.common.core.domain.AjaxResult;
@@ -24,6 +47,7 @@ import com.ylx.massage.domain.MaTechnician;
 import com.ylx.massage.service.IMaTechnicianService;
 import com.ylx.common.utils.poi.ExcelUtil;
 import com.ylx.common.core.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 技师Controller
@@ -34,18 +58,209 @@ import com.ylx.common.core.page.TableDataInfo;
 @Api("技师管理")
 @RestController
 @RequestMapping("/technician/technician")
-public class MaTechnicianController extends BaseController
-{
+public class MaTechnicianController extends BaseController {
     @Autowired
     private IMaTechnicianService maTechnicianService;
 
+    @Autowired
+    private StringRedisTemplate redisTemplate;
+
+    @Autowired
+    private SendSmsComponents sendSms;
+    @Autowired
+    private ServiceCategoryService serviceCategoryService;
+    @Autowired
+    private IMaProjectService maProjectService;
+
+    public static final String PHONE_THREEUSERPARTCLIENT_CODE_KEY = "sys:threeUserPartClient:phone:";
+
+    @GetMapping("/sendMsg")
+    @ApiOperation(value = "短信发送", notes = "短信发送")
+    public Result sendMsg(@RequestParam String phone, HttpServletRequest request) {
+        if (org.apache.commons.lang3.StringUtils.isEmpty(phone)) {
+            return Result.error("手机号不能为空");
+        }
+        Random rand = new Random();
+        // randNumber 将被赋值为一个 MIN 和 MAX 范围内的随机数
+        int randNumber = rand.nextInt(9999 - 1000 + 1) + 1000;
+        // 保存验证码到redis
+        redisTemplate.opsForValue()
+                .set("userH5:order:phone:" + phone, String.valueOf(randNumber), 5L
+                        , TimeUnit.MINUTES);
+        try {
+            SMSVerificationCode smsVerificationCode = new SMSVerificationCode(String.valueOf(randNumber));
+            String jsonString = JSON.toJSONString(smsVerificationCode);
+            sendSms.sendSms(phone, SendSmsEnum.SMS_220650024, jsonString);
+            return Result.ok("发送成功");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return Result.ok("发送失败");
+    }
+
+    /**
+     * 商户登录接口
+     *
+     * @param thirdPartyLoginsVo
+     * @return
+     */
+    @ApiOperation(value = "商户登录", notes = "商户登录")
+    @PostMapping(value = "/clientLogin")
+    @Transactional
+    public Result<JSONObject> login(@RequestBody ThirdPartyLoginsVo thirdPartyLoginsVo) throws Exception {
+        // 获取登录用户信息
+        Result<JSONObject> result = new Result<>();
+
+        // 校验手机号是否为空
+        if (StringUtils.isEmpty(thirdPartyLoginsVo.getPhone())) {
+            return result.error500("请输入手机号");
+        }
+
+        // 校验用户是否存在且有效
+        LambdaQueryWrapper<MaTechnician> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(MaTechnician::getTePhone, thirdPartyLoginsVo.getPhone());
+        MaTechnician maTechnician = maTechnicianService.getBaseMapper().selectOne(queryWrapper);
+        // 校验用户是否有效
+        if (ObjectUtils.isEmpty(maTechnician)) {
+            return result.error500("商户不存在,请先注册");
+
+        }
+
+        if (thirdPartyLoginsVo.getCodeSwitch()) {
+            // 短信验证
+            String msg = redisTemplate.opsForValue().get(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
+            if (StringUtils.isEmpty(msg)) {
+                return Result.error("验证码已失效");
+            }
+
+            if (!thirdPartyLoginsVo.getPhoneMsg().equals(msg)) {
+                return Result.error("短信验证码不正确");
+            }
+        } else {
+            if (!thirdPartyLoginsVo.getPassWord().equals(maTechnician.getTePassword())) {
+                return Result.error("密码错误");
+            }
+
+        }
+
+        // 登录成功删除验证码
+        redisTemplate.delete(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
+        result.success("登录成功");
+        return result;
+    }
+
+    /**
+     * 商户忘记密码接口
+     */
+    @PostMapping("/resetPassword")
+    public Result<?> resetPassword(@RequestBody ThirdPartyLoginsVo thirdPartyLoginsVo) {
+        // 核心正则表达式:
+        // ^ 表示开头,$ 表示结尾
+        // [a-zA-Z0-9] 表示只能是字母或数字
+        // {8,20} 表示长度必须在8到20之间
+        String regex = "^[a-zA-Z0-9]{8,20}$";
+        boolean isMatch = Pattern.matches(regex, thirdPartyLoginsVo.getPhoneMsg());
+        if (!isMatch) {
+            // 根据需求返回指定的异常提示
+            return Result.error("请输入8-20位数字/字母组合");
+        }
+        // 短信验证
+        String msg = redisTemplate.opsForValue().get(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
+        if (StringUtils.isEmpty(msg)) {
+            return Result.error("验证码已失效");
+        }
+        if (!thirdPartyLoginsVo.getPhoneMsg().equals(msg)) {
+            return Result.error("短信验证码不正确");
+        }
+        // 重置密码逻辑
+        LambdaUpdateWrapper<MaTechnician> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(MaTechnician::getTePhone, thirdPartyLoginsVo.getPhone());
+        updateWrapper.set(MaTechnician::getTePassword, thirdPartyLoginsVo.getPassWord());
+        maTechnicianService.update(updateWrapper);
+        redisTemplate.delete(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
+        return Result.ok("重置密码成功");
+    }
+
+    /**
+     * 商户入驻申请接口
+     */
+    @PostMapping("/apply")
+    public Result<?> apply(@RequestBody MaTechnicianAppAddVo req) {
+        // 1. 基础参数校验
+        if (StringUtils.isAnyBlank(req.getTeName(), req.getTePhone(), req.getTeAddress(), req.getAvatar())) {
+            return Result.error("必填项不能为空");
+        }
+        //校验性别不能为空
+        if(Objects.isNull(req.getTeSex())){
+            return Result.error("性别不能为空");
+        }
+        // 2. 调用业务层处理入驻申请
+        maTechnicianService.apply(req);
+        return Result.ok("提交成功,进入审核流程");
+    }
+
+    /**
+     * 查询商户信息接口
+     */
+    @GetMapping("/getTechnician")
+    public Result<?> getTechnician(@RequestParam String phone) {
+        LambdaQueryWrapper<MaTechnician> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(MaTechnician::getTePhone, phone);
+        MaTechnician maTechnician = maTechnicianService.getBaseMapper().selectOne(queryWrapper);
+        return Result.ok(maTechnician);
+    }
+
+    /**
+     * 修改和上传商户信息接口
+     */
+    @PostMapping("/updateTechnician")
+    public Result<?> updateTechnician(@RequestBody MaTechnicianAppAddVo req) {
+        if (req.getAuditStatus() == 0 || req.getAuditStatus() == 3) {
+            //修改基本信息
+            updateMaTechnician(req);
+        } else if (req.getAuditStatus() == 1 || req.getAuditStatus() == 2) {
+            //上传商户资料信息
+            extractedUpdate(req);
+        }
+        return Result.ok("修改成功");
+    }
+
+    private void extractedUpdate(MaTechnicianAppAddVo req) {
+        LambdaUpdateWrapper<MaTechnician> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(MaTechnician::getId, req.getId());
+        updateWrapper.set(MaTechnician::getTeAvatar, req.getTeAvatar());
+        updateWrapper.set(MaTechnician::getTeNickName, req.getTeNickName());
+        updateWrapper.set(MaTechnician::getTeBrief, req.getTeBrief());
+        updateWrapper.set(MaTechnician::getLifePhotos, req.getLifePhotos());
+        updateWrapper.set(MaTechnician::getIdCard, req.getIdCard());
+        updateWrapper.set(MaTechnician::getHealthCertificate, req.getHealthCertificate());
+        updateWrapper.set(MaTechnician::getQualificationCertificate, req.getQualificationCertificate());
+        updateWrapper.set(MaTechnician::getNoCrimeRecord, req.getNoCrimeRecord());
+        updateWrapper.set(MaTechnician::getPromoVideo, req.getPromoVideo());
+        updateWrapper.set(MaTechnician::getCommitmentAudio, req.getCommitmentAudio());
+        updateWrapper.set(MaTechnician::getCommitmentPdf, req.getCommitmentPdf());
+        updateWrapper.set(MaTechnician::getCommitmentVideo, req.getCommitmentVideo());
+        maTechnicianService.update(updateWrapper);
+    }
+
+    private void updateMaTechnician(MaTechnicianAppAddVo req) {
+        LambdaUpdateWrapper<MaTechnician> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(MaTechnician::getId, req.getId());
+        updateWrapper.set(MaTechnician::getTePhone, req.getTePhone());
+        updateWrapper.set(MaTechnician::getTeName, req.getTeName());
+        updateWrapper.set(MaTechnician::getOpenService, req.getOpenService());
+        updateWrapper.set(MaTechnician::getTeAddress, req.getTeAddress());
+        updateWrapper.set(MaTechnician::getTeAge, req.getTeAge());
+        updateWrapper.set(MaTechnician::getAvatar, req.getAvatar());
+        maTechnicianService.update(updateWrapper);
+    }
+
     /**
      * 查询技师列表
      */
     @PreAuthorize("@ss.hasPermi('technician:technician:list')")
     @GetMapping("/list")
-    public TableDataInfo list(MaTechnician maTechnician)
-    {
+    public TableDataInfo list(MaTechnician maTechnician) {
         startPage();
         List<MaTechnician> list = maTechnicianService.selectMaTechnicianList(maTechnician);
         return getDataTable(list);
@@ -57,8 +272,7 @@ public class MaTechnicianController extends BaseController
     @PreAuthorize("@ss.hasPermi('technician:technician:export')")
     @Log(title = "技师", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
-    public void export(HttpServletResponse response, MaTechnician maTechnician)
-    {
+    public void export(HttpServletResponse response, MaTechnician maTechnician) {
         List<MaTechnician> list = maTechnicianService.selectMaTechnicianList(maTechnician);
         ExcelUtil<MaTechnician> util = new ExcelUtil<MaTechnician>(MaTechnician.class);
         util.exportExcel(response, list, "技师数据");
@@ -69,8 +283,7 @@ public class MaTechnicianController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('technician:technician:query')")
     @GetMapping(value = "/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id)
-    {
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
         return success(maTechnicianService.selectMaTechnicianById(id));
     }
 
@@ -81,19 +294,135 @@ public class MaTechnicianController extends BaseController
     @PreAuthorize("@ss.hasPermi('technician:technician:add')")
     @Log(title = "技师", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo)
-    {
+    public AjaxResult add(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo) {
         return toAjax(maTechnicianService.insertMaTechnician(maTechnicianAppAddVo));
     }
 
+    /**
+     * 后台新增商户
+     *
+     * @param dto 商户新增DTO
+     * @return AjaxResult 结果
+     */
+    @ApiOperation("后台新增商户")
+    @PreAuthorize("@ss.hasPermi('technician:technician:add')")
+    @Log(title = "商户", businessType = BusinessType.INSERT)
+    @PostMapping("/merchant")
+    public AjaxResult addMerchant(@RequestBody MaTechnicianMerchantAddDTO dto) {
+        try {
+            LoginUser loginUser = getLoginUser();
+            return toAjax(maTechnicianService.insertMerchant(dto, loginUser));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 后台编辑商户
+     *
+     * @param id  商户ID
+     * @param dto 商户编辑DTO
+     * @return AjaxResult 结果
+     */
+    @ApiOperation("后台编辑商户")
+    @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
+    @Log(title = "商户", businessType = BusinessType.UPDATE)
+    @PutMapping("/merchant/{id}")
+    public AjaxResult editMerchant(@PathVariable("id") Long id, @RequestBody MaTechnicianMerchantAddDTO dto) {
+        try {
+            LoginUser loginUser = getLoginUser();
+            return toAjax(maTechnicianService.updateMerchant(id, dto, loginUser));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 后台上传商户合同文件
+     *
+     * @param id   商户ID
+     * @param file 合同文件
+     * @return AjaxResult 上传结果
+     */
+    @ApiOperation("后台上传商户合同文件")
+    @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
+    @Log(title = "商户合同", businessType = BusinessType.UPDATE)
+    @PostMapping("/merchant/{id}/contract")
+    public AjaxResult uploadMerchantContract(@PathVariable("id") Integer id, @RequestParam("file") MultipartFile file) {
+        try {
+            LoginUser loginUser = getLoginUser();
+            return maTechnicianService.uploadMerchantContract(id, file, loginUser);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 查询商户入驻审核列表
+     *
+     * @param page 分页参数
+     * @param dto  查询条件
+     * @return Page 商户入驻审核分页列表
+     */
+    @ApiOperation("后台查询商户入驻审核列表")
+    @PreAuthorize("@ss.hasPermi('technician:technician:list')")
+    @GetMapping("/merchant/audit/list")
+    public R<Page<MaTechnicianAuditListVO>> merchantAuditList(Page<MaTechnicianAuditListVO> page, MaTechnicianAuditQueryDTO dto) {
+        try {
+            return R.ok(maTechnicianService.selectMerchantAuditList(page, dto));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 查询商户列表
+     *
+     * @param page 分页参数
+     * @param dto  商户查询DTO
+     * @return Page 商户分页列表
+     */
+    @ApiOperation("后台查询商户列表")
+    @PreAuthorize("@ss.hasPermi('technician:technician:list')")
+    @GetMapping("/merchant/list")
+    public R<Page<MaTechnicianMerchantListVO>> merchantList(Page<MaTechnicianMerchantListVO> page, MaTechnicianMerchantQueryDTO dto) {
+        try {
+            return R.ok(maTechnicianService.selectMerchantList(page, dto));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 查询商户详情
+     *
+     * @param id 商户ID
+     * @return R<MaTechnicianMerchantDetailVO> 商户详情
+     */
+    @ApiOperation("后台查询商户详情")
+    @PreAuthorize("@ss.hasPermi('technician:technician:query')")
+    @GetMapping("/merchant/detail/{id}")
+    public R<MaTechnicianMerchantDetailVO> merchantDetail(@PathVariable("id") Long id) {
+        try {
+            return R.ok(maTechnicianService.selectMerchantDetail(id));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * 修改技师
      */
     @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
     @Log(title = "技师", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo)
-    {
+    public AjaxResult edit(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo) {
         return toAjax(maTechnicianService.updateMaTechnician(maTechnicianAppAddVo));
     }
 
@@ -102,9 +431,112 @@ public class MaTechnicianController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('technician:technician:remove')")
     @Log(title = "技师", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{ids}")
-    public AjaxResult remove(@PathVariable Long[] ids)
-    {
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
         return toAjax(maTechnicianService.deleteMaTechnicianByIds(ids));
     }
+
+    /**
+     * 1. 获取服务类目列表 (对应图1、图3)
+     */
+    @GetMapping("/getServiceCategoryList")
+    public AjaxResult getServiceCategoryList() {
+        List<ServiceCategory> list = serviceCategoryService.listH5ServiceCategory();
+        return AjaxResult.success(list);
+    }
+
+    /**
+     * 1. 获取技能列表 (对应图1、图3)
+     * 支持 Tab 切换:all(全部), active(已开通), applying(申请中), rejected(驳回)
+     */
+    @PostMapping("/getSkillList")
+    public TableDataInfo getSkillList(@RequestBody MaProjectGetVo req) {
+        startPage();
+        List<MaProject> list = maTechnicianService.selectMaTechnicianListBy(req.getUserId(), req.getAuditStatus());
+        if (ObjectUtils.isEmpty(list)) {
+            List<Project> projectslist = maTechnicianService.selectTechnicianListBy(req.getTypeId());
+            return getDataTable(projectslist);
+        } else {
+            return getDataTable(list);
+        }
+    }
+
+    /**
+     * 查询未开通的服务项目列表
+     *
+     * @param req
+     * @return
+     */
+    @PostMapping("/getNotApplyList")
+    public Result<?> getNotApplyList(@RequestBody MaProjectGetVo req) {
+
+        return Result.ok(maTechnicianService.getNotApplyList(req.getUserId(), req.getTypeId()));
+
+    }
+
+    /**
+     * 申请开通新服务
+     */
+    @PostMapping("/applyForService")
+    public AjaxResult applyForService(@RequestBody MaProjectSaveDto dto) {
+
+        return toAjax(maTechnicianService.applyForService(dto));
+    }
+
+    /**
+     * 重新申请开通新服务
+     *
+     * @param req
+     * @return
+     */
+    @PostMapping("/updateApply")
+    public Result<?> updateApply(@RequestBody MaProjectUpdateDto req) {
+        if (StringUtils.isNotEmpty(req.getProjectId()) && StringUtils.isNotEmpty(req.getApplyReason())) {
+            LambdaUpdateWrapper<MaProject> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(MaProject::getId, req.getProjectId());
+            updateWrapper.set(MaProject::getApplyReason, req.getApplyReason());
+            updateWrapper.set(MaProject::getAuditStatus, 0);
+            maProjectService.update(updateWrapper);
+        }
+        return Result.ok("重新申请成功,提交到审核阶段");
+
+    }
+
+    /**
+     * 申请下架,删除服务项目,编辑售价价格
+     *
+     * @param req
+     * @return
+     */
+    @PostMapping("/updateMaProject")
+    public Result<?> updateMaProject(@RequestBody MaProjectUpdateDto req) {
+        String message = "";
+        if (StringUtils.isNotEmpty(req.getProjectId())) {
+            if (req.getIsDelete()) {
+                LambdaUpdateWrapper<MaProject> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(MaProject::getId, req.getProjectId());
+                updateWrapper.set(MaProject::getIsDelete, 1);
+                maProjectService.update(updateWrapper);
+                message = "删除成功";
+            }
+            if (req.getIsPass()) {
+                LambdaUpdateWrapper<MaProject> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(MaProject::getId, req.getProjectId());
+                updateWrapper.set(MaProject::getProjectIsEnable, 1);
+                maProjectService.update(updateWrapper);
+                message = "申请下架成功";
+            }
+            if (req.getProjectCurrentPrice() != null) {
+
+                LambdaUpdateWrapper<MaProject> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(MaProject::getId, req.getProjectId());
+                updateWrapper.set(MaProject::getProjectCurrentPrice, req.getProjectCurrentPrice());
+                maProjectService.update(updateWrapper);
+                message = "修改价格完成";
+
+            }
+        }
+        return Result.ok(message);
+    }
+
 }

+ 149 - 292
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/PayController.java

@@ -2,57 +2,52 @@ package com.ylx.web.controller.massage;
 
 import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.io.file.FileWriter;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.ContentType;
-import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
 import com.ijpay.core.IJPayHttpResponse;
-import com.ijpay.core.enums.AuthTypeEnum;
 import com.ijpay.core.enums.RequestMethodEnum;
-import com.ijpay.core.kit.AesUtil;
 import com.ijpay.core.kit.HttpKit;
 import com.ijpay.core.kit.PayKit;
 import com.ijpay.core.kit.WxPayKit;
-import com.ijpay.core.utils.DateTimeZoneUtil;
 import com.ijpay.wxpay.WxPayApi;
-import com.ijpay.wxpay.WxPayApiConfigKit;
 import com.ijpay.wxpay.enums.WxDomainEnum;
-import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
-import com.ijpay.wxpay.enums.v3.CertAlgorithmTypeEnum;
 import com.ijpay.wxpay.enums.v3.TransferApiEnum;
-import com.ijpay.wxpay.model.v3.*;
-import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiServiceExtension;
+import com.ijpay.wxpay.model.v3.BatchTransferModel;
+import com.ijpay.wxpay.model.v3.TransferDetailInput;
 import com.ylx.common.config.WxPayConfig;
 import com.ylx.common.core.domain.R;
+import com.ylx.common.weixinPay.enums.WxPayTypeEnum;
+import com.ylx.giftCard.domain.GiftCardOrder;
+import com.ylx.giftCard.enums.GiftCardOrderStatusEnum;
+import com.ylx.giftCard.service.IGiftCardOrderService;
 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;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.ByteArrayInputStream;
 import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.security.cert.X509Certificate;
-import java.util.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 import static com.ylx.common.constant.HttpStatus.SUCCESS;
 
@@ -67,29 +62,19 @@ import static com.ylx.common.constant.HttpStatus.SUCCESS;
 @Api(tags = {"微信支付"})
 public class PayController {
 
-
-    @Autowired
+    @Resource
     private WxPayConfig wxPayProperties;
-
     @Resource
     private TRechargeService rechargeService;
-
-    @Resource
-    private TOrderService orderService;
-
     @Resource
     private RefundVoucherService refundVoucherService;
-
-    @Resource
-    private IProductOrderInfoService productOrderInfoService;
-
     @Resource
     private TWxUserService wxUserService;
-
-    //    @Resource
-//    private JsapiServiceExtension service;
     String serialNo;
-    String platSerialNo;
+    @Resource
+    private WxPayService wxPayService;
+    @Resource
+    private IGiftCardOrderService giftCardOrderService;
 
     /**
      * 小程序微信支付的第一步,统一下单
@@ -101,118 +86,9 @@ public class PayController {
         return rechargeService.getPay(rechargeResp.getRechargeNo(), recharge.getdMoney(), recharge.getcOpenId(), BillTypeEnum.RECHARGE.getInfo(), BillTypeEnum.RECHARGE.getCode().toString());
     }
 
-    /**
-     * 支付
-     *
-     * @param setOutTradeNo
-     * @param amount
-     * @param openId
-     * @param description
-     * @return
-     * @throws Exception
-     */
-    public R<String> getPay(String setOutTradeNo, BigDecimal amount, String openId, String description) throws Exception {
-        String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
-        UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
-                .setAppid(wxPayProperties.getAppId())
-                .setMchid(wxPayProperties.getMchId())
-                //商品描述
-                .setDescription(description)
-                //订单号
-                .setOut_trade_no(setOutTradeNo)
-                //交易结束时间
-                .setTime_expire(timeExpire)
-                //附加数据
-                .setAttach("夜来香")
-                //通知地址异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
-                //示例值:https://www.weixin.qq.com/wxpay/pay.php
-                .setNotify_url(wxPayProperties.getNotifyUrl())
-                //支付金额以分为单位
-                .setAmount(new Amount().setTotal(amount.multiply(new BigDecimal(100)).intValue()))
-                //交易人
-                .setPayer(new Payer().setOpenid(openId));
-
-        log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
-        IJPayHttpResponse response = WxPayApi.v3(
-                RequestMethodEnum.POST,
-                WxDomainEnum.CHINA.toString(),
-                BasePayApiEnum.JS_API_PAY.toString(),
-                wxPayProperties.getMchId(),
-                getSerialNumber(),
-                null,
-                wxPayProperties.getCertKeyPath(),
-                JSONUtil.toJsonStr(unifiedOrderModel)
-        );
-
-        log.info("统一下单响应 {}", response);
-        // 根据证书序列号查询对应的证书来验证签名结果
-        boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
-        log.info("verifySignature: {}", verifySignature);
-        if (response.getStatus() == SUCCESS && verifySignature) {
-            String body = response.getBody();
-            JSONObject jsonObject = JSONUtil.parseObj(body);
-            String prepayId = jsonObject.getStr("prepay_id");
-            Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayProperties.getAppId(), prepayId, wxPayProperties.getCertKeyPath());
-            log.info("唤起支付参数:{}", map);
-            return R.ok(JSONUtil.toJsonStr(map));
-        }
-        return R.ok(JSONUtil.toJsonStr(response));
-    }
-
-    //服务商模式下单
-    public R<String> getPay1(String setOutTradeNo, BigDecimal amount, String openId, String description) throws Exception {
-        String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
-        UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
-                .setSp_appid(wxPayProperties.getAppId())
-                .setSp_mchid(wxPayProperties.getMchId())
-                .setSub_mchid("123")//子商户号
-                //商品描述
-                .setDescription(description)
-                //订单号
-                .setOut_trade_no(setOutTradeNo)
-                //交易结束时间
-                .setTime_expire(timeExpire)
-                //附加数据
-                .setAttach("夜来香")
-                //通知地址异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
-                //示例值:https://www.weixin.qq.com/wxpay/pay.php
-                .setNotify_url(wxPayProperties.getNotifyUrl())
-                //分账标识
-                .setSettle_info(new SettleInfo().setProfit_sharing(true))
-                //支付金额以分为单位
-                .setAmount(new Amount().setTotal(amount.multiply(new BigDecimal(100)).intValue()))
-                //交易人
-                .setPayer(new Payer().setSp_openid(openId));
-
-        log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
-        IJPayHttpResponse response = WxPayApi.v3(
-                RequestMethodEnum.POST,
-                WxDomainEnum.CHINA.toString(),
-                BasePayApiEnum.PARTNER_JS_API_PAY.toString(),
-                wxPayProperties.getMchId(),
-                getSerialNumber(),
-                null,
-                wxPayProperties.getCertKeyPath(),
-                JSONUtil.toJsonStr(unifiedOrderModel)
-        );
-
-        log.info("统一下单响应 {}", response);
-        // 根据证书序列号查询对应的证书来验证签名结果
-        boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
-        log.info("verifySignature: {}", verifySignature);
-        if (response.getStatus() == SUCCESS && verifySignature) {
-            String body = response.getBody();
-            JSONObject jsonObject = JSONUtil.parseObj(body);
-            String prepayId = jsonObject.getStr("prepay_id");
-            Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayProperties.getAppId(), prepayId, wxPayProperties.getCertKeyPath());
-            log.info("唤起支付参数:{}", map);
-            return R.ok(JSONUtil.toJsonStr(map));
-        }
-        return R.ok(JSONUtil.toJsonStr(response));
-    }
-
     /**
      * 获取商户API证书序列号
+     *
      * @return String 证书序列号
      */
     private String getSerialNumber() {
@@ -225,175 +101,155 @@ public class PayController {
                 boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayProperties.getMchId(), -2);
                 log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
             }
-//            System.out.println("输出证书信息:\n" + certificate.toString());
-//            // 输出关键信息,截取部分并进行标记
-//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
-//            System.out.println("版本号:" + certificate.getVersion());
-//            System.out.println("签发者:" + certificate.getIssuerDN());
-//            System.out.println("有效起始日期:" + certificate.getNotBefore());
-//            System.out.println("有效终止日期:" + certificate.getNotAfter());
-//            System.out.println("主体名:" + certificate.getSubjectDN());
-//            System.out.println("签名算法:" + certificate.getSigAlgName());
-//            System.out.println("签名:" + certificate.getSignature().toString());
         }
         System.out.println("serialNo:" + serialNo);
         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("微信支付回调接口")
-    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
-        log.info("微信支付回调接口====================================>>>>微信支付回调接口");
-        Map<String, String> map = new HashMap<>(12);
+//    /**
+//     * 微信支付回调接口
+//     *
+//     * @param request
+//     * @param response
+//     */
+//    @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
+//    @ResponseBody
+//    @ApiOperation("微信支付回调接口")
+//    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
+//        log.info("微信支付回调接口====================================>>>>微信支付回调接口");
+//        Map<String, String> map = new HashMap<>(12);
+//        try {
+//            String timestamp = request.getHeader("Wechatpay-Timestamp");
+//            String nonce = request.getHeader("Wechatpay-Nonce");
+//            String serialNo = request.getHeader("Wechatpay-Serial");
+//            String signature = request.getHeader("Wechatpay-Signature");
+//
+//            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
+//            String result = HttpKit.readData(request);
+//            log.info("支付通知密文 {}", result);
+//
+//            // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
+//            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
+//                    wxPayProperties.getMchKey(), wxPayProperties.getPlatFormPath());
+//
+//            log.info("支付通知明文 {}", plainText);
+//
+//            if (StrUtil.isNotEmpty(plainText)) {
+//                response.setStatus(200);
+//                map.put("code", "SUCCESS");
+//                map.put("message", "SUCCESS");
+//                // 处理业务逻辑
+//                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());
+//                }
+//            } else {
+//                response.setStatus(500);
+//                map.put("code", "ERROR");
+//                map.put("message", "签名错误");
+//            }
+//            response.setHeader("Content-type", ContentType.JSON.toString());
+//            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
+//            response.flushBuffer();
+//        } catch (Exception e) {
+//            log.error("系统异常", e);
+//        }
+//    }
+
+    @PostMapping("/payNotify")
+    public Map<String, String> handlePayNotify(@RequestBody String notifyData, @RequestHeader("Wechatpay-Signature") SignatureHeader signature) {
+
+        Map<String, String> resp = new HashMap<>();
         try {
-            String timestamp = request.getHeader("Wechatpay-Timestamp");
-            String nonce = request.getHeader("Wechatpay-Nonce");
-            String serialNo = request.getHeader("Wechatpay-Serial");
-            String signature = request.getHeader("Wechatpay-Signature");
+            // 1. SDK验签+解密报文
+            WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayService.parseOrderNotifyV3Result(notifyData, signature).getResult();
 
-            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
-            String result = HttpKit.readData(request);
-            log.info("支付通知密文 {}", result);
+            String outTradeNo = result.getOutTradeNo();
+            String tradeState = result.getTradeState();
 
-            // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
-            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
-                    wxPayProperties.getMchKey(), wxPayProperties.getPlatFormPath());
+            log.info("微信V3回调解密成功,商户订单号:{},微信单号:{},交易状态:{}",
+                    outTradeNo, result.getTransactionId(), tradeState);
 
-            log.info("支付通知明文 {}", plainText);
+            // 2. 仅SUCCESS才处理订单
+            if ("SUCCESS".equals(tradeState)) {
 
-            if (StrUtil.isNotEmpty(plainText)) {
-                response.setStatus(200);
-                map.put("code", "SUCCESS");
-                map.put("message", "SUCCESS");
-                // 处理业务逻辑
-                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());
+                // 2. 获取我们在下单时传入的 attach
+                String attach = result.getAttach();
+                String openid = result.getPayer().getOpenid();
+
+                if (StrUtil.isEmpty(openid)) {
+                    log.error("openid不存在");
+                    resp.put("code", "FAIL");
+                    resp.put("message", "openid不存在");
+                    return resp;
                 }
-            } else {
-                response.setStatus(500);
-                map.put("code", "ERROR");
-                map.put("message", "签名错误");
-            }
-            response.setHeader("Content-type", ContentType.JSON.toString());
-            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
-            response.flushBuffer();
-        } catch (Exception e) {
-            log.error("系统异常", e);
-        }
-    }
 
+                log.info("支付成功用户openId,openId: {}", openid);
 
-    @RequestMapping(value = "/test", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
-    @ResponseBody
-    @ApiOperation("测试")
-    public void test(HttpServletRequest request, HttpServletResponse response) {
-        System.out.println("test=======================>");
-    }
+                TWxUser wxUser = wxUserService.getByOpenId(openid);
+                if (ObjectUtil.isNull(wxUser)) {
+                    log.error("支付成功用户不存在,openId: {}", openid);
+                    resp.put("code", "FAIL");
+                    resp.put("message", "支付成功用户不存在");
+                    return resp;
+                }
 
-    @RequestMapping("/get")
-    @ResponseBody
-    public String v3Get() throws Exception {
-        // 获取平台证书列表
-        try {
-            IJPayHttpResponse response = WxPayApi.v3(
-                    RequestMethodEnum.GET,
-                    WxDomainEnum.CHINA.toString(),
-                    CertAlgorithmTypeEnum.getCertSuffixUrl(CertAlgorithmTypeEnum.RSA.getCode()),
-                    wxPayProperties.getMchId(),
-                    getSerialNumber(),
-                    null,
-                    wxPayProperties.getCertKeyPath(),
-                    "",
-                    AuthTypeEnum.RSA.getCode()
-            );
-            Map<String, List<String>> headers = response.getHeaders();
-            log.info("请求头: {}", headers);
-            String timestamp = response.getHeader("Wechatpay-Timestamp");
-            String nonceStr = response.getHeader("Wechatpay-Nonce");
-            String serialNumber = response.getHeader("Wechatpay-Serial");
-            String signature = response.getHeader("Wechatpay-Signature");
-
-            String body = response.getBody();
-            int status = response.getStatus();
-
-            log.info("serialNumber: {}", serialNumber);
-            log.info("status: {}", status);
-            log.info("body: {}", body);
-            int isOk = 200;
-            if (status == isOk) {
-                JSONObject jsonObject = JSONUtil.parseObj(body);
-                JSONArray dataArray = jsonObject.getJSONArray("data");
-                // 默认认为只有一个平台证书
-                JSONObject encryptObject = dataArray.getJSONObject(0);
-                JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
-                String associatedData = encryptCertificate.getStr("associated_data");
-                String cipherText = encryptCertificate.getStr("ciphertext");
-                String nonce = encryptCertificate.getStr("nonce");
-                String algorithm = encryptCertificate.getStr("algorithm");
-                String serialNo = encryptObject.getStr("serial_no");
-                final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, algorithm, wxPayProperties.getPlatFormPath());
-                log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
-                // 根据证书序列号查询对应的证书来验证签名结果
-                boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
-                log.info("verifySignature:{}", verifySignature);
+                // 3. 根据 attach 判断商品类型并进行不同的业务处理
+                if (WxPayTypeEnum.GIFT_CARD.getCode().equals(attach)) {
+                    log.info("检测到购物卡支付成功,订单号: {}", outTradeNo);
+
+                    // 3.1 更新订单支付状态
+                    LambdaQueryWrapper<GiftCardOrder> wrapper = new LambdaQueryWrapper<>();
+                    wrapper.eq(GiftCardOrder::getOrderNo, outTradeNo);
+                    GiftCardOrder cardOrder = this.giftCardOrderService.getOne(wrapper);
+                    if (ObjectUtil.isNull(cardOrder)) {
+                        log.error("订单不存在,订单号: {}", outTradeNo);
+                        resp.put("code", "FAIL");
+                        resp.put("message", "订单不存在");
+                        return resp;
+                    }
+                    // 3.2 检查是否已处理
+                    if (ObjectUtil.equals(GiftCardOrderStatusEnum.PAID.getCode(), cardOrder.getStatus())) {
+                        log.warn("订单已处理过:{}", outTradeNo);
+                        resp.put("code", "SUCCESS");
+                        resp.put("message", "OK");
+                        return resp;
+                    }
+
+                    // 3.3 处理订单相关数据
+                    this.giftCardOrderService.processGiftCardPayment(result, wxUser, cardOrder);
+                } else if (WxPayTypeEnum.EMOTION_GOODS.getCode().equals(attach)) {
+                    log.info("检测到情感服务商品支付成功,订单号: {}", outTradeNo);
+                }
             }
-            return body;
-        } catch (Exception e) {
-            log.error("获取平台证书列表异常", e);
-            return null;
-        }
-    }
 
-    private String savePlatformCert(String associatedData, String nonce, String cipherText, String algorithm, String certPath) {
-        try {
-            String key3 = wxPayProperties.getMchKey();
-            String publicKey;
-            if (StrUtil.equals(algorithm, AuthTypeEnum.SM2.getPlatformCertAlgorithm())) {
-                publicKey = PayKit.sm4DecryptToString(key3, cipherText, nonce, associatedData);
-            } else {
-                AesUtil aesUtil = new AesUtil(wxPayProperties.getMchKey().getBytes(StandardCharsets.UTF_8));
-                // 平台证书密文解密
-                // encrypt_certificate 中的  associated_data nonce  ciphertext
-                publicKey = aesUtil.decryptToString(
-                        associatedData.getBytes(StandardCharsets.UTF_8),
-                        nonce.getBytes(StandardCharsets.UTF_8),
-                        cipherText
-                );
-            }
-            if (StrUtil.isNotEmpty(publicKey)) {
-                // 保存证书
-                FileWriter writer = new FileWriter(certPath);
-                writer.write(publicKey);
-                // 获取平台证书序列号
-                X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
-                return certificate.getSerialNumber().toString(16).toUpperCase();
-            }
-            return "";
+            //3. 返回成功
+            resp.put("code", "SUCCESS");
+            resp.put("message", "OK");
+        } catch (WxPayException e) {
+            log.error("微信支付回调异常:{}", e.getMessage(), e);
+            resp.put("code", "FAIL");
+            resp.put("message", e.getMessage());
         } catch (Exception e) {
-            log.error("保存平台证书异常", e);
-            return e.getMessage();
+            log.error("支付回调处理异常:{}", e.getMessage(), e);
+            resp.put("code", "FAIL");
+            resp.put("message", "系统异常");
         }
+        return resp;
     }
 
     /**
      * 微信批量提现
+     *
      * @param openId
      * @return String
      */
@@ -463,6 +319,7 @@ public class PayController {
 
     /**
      * 微信退款回调接口
+     *
      * @param request
      * @param response
      */

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

@@ -6,14 +6,13 @@ 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.core.domain.model.WxLoginUser;
 import com.ylx.common.enums.BusinessType;
 import com.ylx.common.utils.StringUtils;
 
 import com.ylx.massage.domain.TComment;
 
 import com.ylx.massage.service.TCommentService;
-import com.ylx.massage.service.TOrderService;
+import com.ylx.order.service.TOrderService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiOperation;

+ 4 - 24
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TFinancialIncomeController.java

@@ -1,42 +1,22 @@
 package com.ylx.web.controller.massage;
 
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-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.core.domain.model.LoginUser;
 import com.ylx.common.enums.BusinessType;
-import com.ylx.common.exception.ServiceException;
-import com.ylx.common.utils.StringUtils;
 import com.ylx.common.utils.poi.ExcelUtil;
-import com.ylx.massage.domain.OrderAllocationLog;
-import com.ylx.massage.domain.TJs;
-import com.ylx.massage.domain.TOrder;
-import com.ylx.massage.domain.vo.OrderAllocationResultVo;
-import com.ylx.massage.domain.vo.OrderVerificationVo;
-import com.ylx.massage.domain.vo.TechnicianAvailabilityVo;
-import com.ylx.massage.enums.Enumproject;
-import com.ylx.massage.enums.OrderStatusEnum;
-import com.ylx.massage.enums.OrderStatusEnumVo;
-import com.ylx.massage.service.OrderAllocationLogService;
-import com.ylx.massage.service.TJsService;
-import com.ylx.massage.service.TOrderService;
+import com.ylx.order.domain.TOrder;
+import com.ylx.order.service.TOrderService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.BeanUtils;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
 
 /**
  * 订单表 前端控制器
@@ -63,7 +43,7 @@ public class TFinancialIncomeController extends BaseController {
         try {
             LoginUser loginUser = this.getLoginUser();
             log.info("登录用户信息:{}", JSON.toJSONString(loginUser));
-            order.setDeptId(loginUser.getDeptId().toString());
+//            order.setDeptId(loginUser.getDeptId().toString());
             Page<TOrder> all = orderService.getPcOrderIncome(page, order);
             return R.ok(all);
         } catch (Exception e) {
@@ -78,7 +58,7 @@ public class TFinancialIncomeController extends BaseController {
     public void export(HttpServletResponse response, Page<TOrder> page, TOrder param) {
         LoginUser loginUser = this.getLoginUser();
         log.info("登录用户信息:{}", JSON.toJSONString(loginUser));
-        param.setDeptId(loginUser.getDeptId().toString());
+//        param.setDeptId(loginUser.getDeptId().toString());
         Page<TOrder> all = orderService.getAll(page, param);
         ExcelUtil<TOrder> util = new ExcelUtil<>(TOrder.class);
         util.exportExcel(response, all.getRecords(), "订单");

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

@@ -13,7 +13,7 @@ import com.ylx.common.exception.ServiceException;
 import com.ylx.common.utils.StringUtils;
 import com.ylx.common.utils.poi.ExcelUtil;
 import com.ylx.massage.domain.TJs;
-import com.ylx.massage.domain.TOrder;
+import com.ylx.order.domain.TOrder;
 import com.ylx.massage.domain.TWxUser;
 import com.ylx.massage.domain.vo.OrderAllocationResultVo;
 import com.ylx.massage.domain.vo.OrderVerificationVo;
@@ -23,11 +23,10 @@ import com.ylx.massage.enums.OrderStatusEnum;
 import com.ylx.massage.enums.OrderStatusEnumVo;
 import com.ylx.massage.domain.OrderAllocationLog;
 import com.ylx.massage.service.TJsService;
-import com.ylx.massage.service.TOrderService;
+import com.ylx.order.service.TOrderService;
 import com.ylx.massage.service.OrderAllocationLogService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.web.bind.annotation.*;
@@ -37,7 +36,7 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
-import java.time.YearMonth;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -199,7 +198,7 @@ public class TOrderController extends BaseController {
     @RequestMapping(value = "wx/cancelApplyCancle", method = RequestMethod.POST)
     public R cancelApplyCancle(@RequestBody TOrder order) {
         try {
-            log.info("收到取消退单申请请求,订单ID:{}", order.getcId());
+            log.info("收到取消退单申请请求,订单ID:{}", order.getId());
             // 调用服务层处理取消退单申请
             orderService.cancelApplyCancle(order);
             // 返回成功提示
@@ -319,11 +318,11 @@ public class TOrderController extends BaseController {
             log.info("到达订单位置,order对象的值:{}", JSON.toJSONString(order));
 
             LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
-            wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.DEPART.getCode());
-            order.setnStatus(OrderStatusEnum.ARRIVED.getCode());
-            order.setReachTime(LocalDateTime.now());
-            order.setArrivalLatitude(Optional.ofNullable(order.getArrivalLatitude()).orElse(BigDecimal.ZERO));
-            order.setArrivalLongitude(Optional.ofNullable(order.getArrivalLongitude()).orElse(BigDecimal.ZERO));
+//            wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.DEPART.getCode());
+//            order.setnStatus(OrderStatusEnum.ARRIVED.getCode());
+//            order.setReachTime(LocalDateTime.now());
+//            order.setArrivalLatitude(Optional.ofNullable(order.getArrivalLatitude()).orElse(BigDecimal.ZERO));
+//            order.setArrivalLongitude(Optional.ofNullable(order.getArrivalLongitude()).orElse(BigDecimal.ZERO));
             order.setArrivalPhoto(Optional.ofNullable(order.getArrivalPhoto()).orElse(StringUtils.EMPTY));
             return R.ok(orderService.update(order, wrapper));
         } catch (ServiceException s) {
@@ -348,9 +347,9 @@ public class TOrderController extends BaseController {
     public R service(@RequestBody TOrder order) {
         try {
             LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
-            wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.ARRIVED.getCode());
-            // 设置订单状态:服务中
-            order.setnStatus(OrderStatusEnum.SERVICE.getCode());
+//            wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.ARRIVED.getCode());
+//            // 设置订单状态:服务中
+//            order.setnStatus(OrderStatusEnum.SERVICE.getCode());
             order.setStartTime(LocalDateTime.now());
             return R.ok(orderService.update(order, wrapper));
         } catch (ServiceException s) {
@@ -410,9 +409,10 @@ public class TOrderController extends BaseController {
     @ApiOperation("技师端-获取待接单数量")
     @RequestMapping(value = "wx/waitOrder", method = RequestMethod.GET)
     public R waitOrder(@RequestParam String cJsId) {
-        List<TOrder> list = orderService.list(new LambdaQueryWrapper<TOrder>().
-                eq(TOrder::getnStatus, OrderStatusEnum.WAIT_JD.getCode()).
-                eq(TOrder::getcJsId, cJsId));
+        List<TOrder> list = new ArrayList<>();
+//                List<TOrder> list = orderService.list(new LambdaQueryWrapper<TOrder>().
+//                eq(TOrder::getnStatus, OrderStatusEnum.WAIT_JD.getCode()).
+//                eq(TOrder::getcJsId, cJsId));
         return R.ok(list.size());
     }
 
@@ -431,7 +431,7 @@ public class TOrderController extends BaseController {
         try {
             LoginUser loginUser = this.getLoginUser();
             log.info("登录用户信息:{}", JSON.toJSONString(loginUser));
-            order.setDeptId(loginUser.getDeptId().toString());
+//            order.setDeptId(loginUser.getDeptId().toString());
             Page<TOrder> all = orderService.getAll(page, order);
             return R.ok(all);
         } catch (Exception e) {
@@ -460,9 +460,9 @@ public class TOrderController extends BaseController {
     @RequestMapping(value = "/select", method = RequestMethod.GET)
     public R selectSp(Page<TOrder> page, TOrder order) {
         LambdaQueryWrapper<TOrder> tOrderLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        tOrderLambdaQueryWrapper.eq(StringUtils.isNotBlank(order.getcJsId()), TOrder::getcJsId, order.getcJsId()).
-                eq(StringUtils.isNotBlank(order.getcOpenId()), TOrder::getcOpenId, order.getcOpenId()).
-                eq(null != order.getnStatus(), TOrder::getnStatus, order.getnStatus());
+//        tOrderLambdaQueryWrapper.eq(StringUtils.isNotBlank(order.getcJsId()), TOrder::getcJsId, order.getcJsId()).
+//                eq(StringUtils.isNotBlank(order.getcOpenId()), TOrder::getcOpenId, order.getcOpenId()).
+//                eq(null != order.getnStatus(), TOrder::getnStatus, order.getnStatus());
         // 获取查询返回结果
         Page<TOrder> pageSelect = orderService.page(page, tOrderLambdaQueryWrapper);
         return R.ok(pageSelect);
@@ -502,7 +502,7 @@ public class TOrderController extends BaseController {
     @RequestMapping(value = "/del", method = RequestMethod.POST)
     public R del(@RequestBody TOrder borrow) {
         try {
-            log.info("删除订单ID:{}", borrow.getcId());
+            log.info("删除订单ID:{}", borrow.getId());
             return R.ok(orderService.removeById(borrow));
         } catch (Exception e) {
             e.printStackTrace();
@@ -520,7 +520,7 @@ public class TOrderController extends BaseController {
     @RequestMapping(value = "/getByid", method = RequestMethod.POST)
     public R<TOrder> getByid(@RequestBody TOrder borrow) {
         try {
-            return R.ok(orderService.getById(borrow.getcId()));
+            return R.ok(orderService.getById(borrow.getId()));
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -621,7 +621,7 @@ public class TOrderController extends BaseController {
         LoginUser loginUser = this.getLoginUser();
         log.info("登录用户信息:{}", JSON.toJSONString(loginUser));
         TOrder order = new TOrder();
-        order.setDeptId(loginUser.getDeptId().toString());
+//        order.setDeptId(loginUser.getDeptId().toString());
         return R.ok(orderService.totalData(order));
     }
 

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

@@ -10,7 +10,6 @@ import com.ylx.common.core.domain.R;
 import com.ylx.common.enums.BusinessType;
 import com.ylx.common.exception.ServiceException;
 import com.ylx.common.utils.poi.ExcelUtil;
-import com.ylx.massage.domain.TOrder;
 import com.ylx.massage.domain.TTxRecord;
 import com.ylx.massage.enums.TTxRecordEnum;
 import com.ylx.massage.service.TTxRecordService;

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

@@ -660,7 +660,7 @@ public class WeChatController extends BaseController {
         try {
             SMSVerificationCode smsVerificationCode = new SMSVerificationCode(String.valueOf(randNumber));
             String jsonString = JSON.toJSONString(smsVerificationCode);
-            sendSms.sendSms(phone, SendSmsEnum.SMS_220650023, jsonString);
+            sendSms.sendSms(phone, SendSmsEnum.SMS_220650024, jsonString);
             return R.ok("发送成功");
         } catch (Exception e) {
             e.printStackTrace();

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

@@ -1,5 +1,9 @@
 package com.ylx.web.controller.massage;
 
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
+import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
 import cn.hutool.json.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ylx.common.annotation.Log;
@@ -14,8 +18,10 @@ 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.MaTechnician;
 import com.ylx.massage.domain.TWxUser;
 import com.ylx.massage.service.CouponReceiveService;
+import com.ylx.massage.service.IMaTechnicianService;
 import com.ylx.massage.service.TWxUserService;
 import com.ylx.massage.utils.WeChatUtil;
 import io.swagger.annotations.Api;
@@ -63,8 +69,10 @@ public class WeSqController extends BaseController {
 
     @Autowired
     public RedisTemplate redisTemplate;
-
-
+    @Autowired
+    private IMaTechnicianService maTechnicianService;
+    @Autowired
+    private WxMaService wxMaService;
     /**
      * 通过微信code获取token和userInfo
      *
@@ -132,4 +140,71 @@ public class WeSqController extends BaseController {
         }
     }
 
+    /**
+     * 通过微信code获取token和userInfo
+     *
+     * @param code 微信授权码
+     * @return R<WxLoginUser> 访问令牌
+     */
+    @GetMapping("/getTechnicianToken")
+    @ResponseBody
+    //@Log(title = "公众号网页登录", businessType = BusinessType.OTHER)
+    public R<WxLoginUser> getTechnicianToken(@RequestParam String code) {
+        // 发送get请求获取 AccessToken
+        try {
+            Map<?, ?> result = weChatUtil.getAccessToken(code);
+           log.info("result的值:{}", result);
+            String accessToken = result.get(ACCESS_TOKEN).toString();
+            log.info("accessToken的值:{}", accessToken);
+            String refreshToken = result.get(REFRESH_TOKEN).toString();
+            String openid = result.get(OPEN_ID).toString();
+//
+//            // 如果用户是第一次进行微信公众号授权
+//            // 进行这一步时用户应点击了同意授权按钮
+            String userInfoJsom = weChatUtil.getUserInfo(accessToken, openid);
+//            // 解析JSON数据
+           JSONObject jsonObject = new JSONObject(userInfoJsom);
+            //log.info("公众号网页登录:{}", jsonObject);
+            String nickName = jsonObject.get("nickname").toString();
+            String avatarUrl = jsonObject.get("headimgurl").toString();
+            String phoneNumber = jsonObject.get("phoneNumber").toString();
+
+            // 将用户信息保存到数据库中
+            LambdaQueryWrapper<MaTechnician> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            objectLambdaQueryWrapper.eq(MaTechnician::getCOpenid, openid);
+            MaTechnician user = maTechnicianService.getOne(objectLambdaQueryWrapper);
+            if (user == null || StringUtils.isEmpty(user.getTeNickName())) {
+                if (user == null) {
+                    user = new MaTechnician();
+                    user.setCOpenid(openid);
+                    user.setTeNickName(nickName);
+                    user.setTeAvatar(avatarUrl);
+                    user.setTePhone(phoneNumber);
+                    user.setOpenService("-1");
+                    maTechnicianService.save(user);
+                    //异步 添加新人优惠卷
+                    //                threadPoolTaskExecutor.submit(() -> couponReceiveService.submit(new CouponReceive().setOpenid(finalUser.getcOpenid()).setCouponId("1")));
+                }
+            }
+            WxLoginUser wxUser = new WxLoginUser();
+            BeanUtils.copyProperties(user, wxUser);
+            // 生成并返回令牌
+            String token = wxTokenService.createToken(wxUser);
+            log.info("token的值:{}", token);
+            if (token == null || token.isEmpty()) {
+                return R.fail("生成令牌失败");
+            }
+            //给我把token的值保存到redis中
+            redisTemplate.opsForValue().set(wxUser.getCOpenid(), token, 180, TimeUnit.MINUTES);
+            wxUser.setToken(token);
+            // 返回用户信息
+            // 记录登录信息
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(wxUser.getCOpenid(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
+            return R.ok(wxUser);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
 }

+ 3 - 0
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/WxController.java

@@ -47,6 +47,7 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -265,11 +266,13 @@ public class WxController extends BaseController {
             objectLambdaQueryWrapper.eq(TWxUser::getcOpenid, openid);
             TWxUser user = wxUserService.getOne(objectLambdaQueryWrapper);
             if (user == null) {
+                BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
                 user = new TWxUser();
                 user.setcOpenid(openid);
                 user.setcNickName(nickName);
                 user.setcIcon(avatarUrl);
                 user.setcPhone(phoneNumber);
+                user.setCPassword(encoder.encode("123456")) ;
                 wxUserService.save(user);
                 //异步 添加新人优惠卷
                 TWxUser finalUser = user;

+ 2 - 6
nightFragrance-admin/src/main/java/com/ylx/web/controller/point/UserPointController.java

@@ -17,12 +17,14 @@ 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.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import java.util.List;
 
+@PreAuthorize("@customerAuth.isCustomer()")
 @RestController
 @RequestMapping("/user/point")
 @Api(tags = {"用户积分"})
@@ -37,12 +39,6 @@ public class UserPointController extends BaseController {
     @Resource
     private IPointAccountService pointAccountService;
 
-    /**
-     * 获取当前用户的积分信息
-     *
-     * @param cityCode
-     * @return R<UserPointInfoVO>
-     */
     @ApiOperation("获取当前用户的积分信息")
     @GetMapping
     public R<UserPointInfoVO> getUserPointInfo(@RequestParam String cityCode) {

+ 11 - 19
nightFragrance-admin/src/main/java/com/ylx/web/controller/system/SysDictDataController.java

@@ -3,6 +3,7 @@ package com.ylx.web.controller.system;
 import java.util.ArrayList;
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
@@ -26,14 +27,13 @@ import com.ylx.system.service.ISysDictDataService;
 import com.ylx.system.service.ISysDictTypeService;
 
 /**
- * 数据字典信息
+ * 字典数据
  *
  * @author ylx
  */
 @RestController
 @RequestMapping("/system/dict/data")
-public class SysDictDataController extends BaseController
-{
+public class SysDictDataController extends BaseController {
     @Autowired
     private ISysDictDataService dictDataService;
 
@@ -42,8 +42,7 @@ public class SysDictDataController extends BaseController
 
     @PreAuthorize("@ss.hasPermi('system:dict:list')")
     @GetMapping("/list")
-    public TableDataInfo list(SysDictData dictData)
-    {
+    public TableDataInfo list(SysDictData dictData) {
         startPage();
         List<SysDictData> list = dictDataService.selectDictDataList(dictData);
         return getDataTable(list);
@@ -52,8 +51,7 @@ public class SysDictDataController extends BaseController
     @Log(title = "字典数据", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:dict:export')")
     @PostMapping("/export")
-    public void export(HttpServletResponse response, SysDictData dictData)
-    {
+    public void export(HttpServletResponse response, SysDictData dictData) {
         List<SysDictData> list = dictDataService.selectDictDataList(dictData);
         ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
         util.exportExcel(response, list, "字典数据");
@@ -64,8 +62,7 @@ public class SysDictDataController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('system:dict:query')")
     @GetMapping(value = "/{dictCode}")
-    public AjaxResult getInfo(@PathVariable Long dictCode)
-    {
+    public AjaxResult getInfo(@PathVariable Long dictCode) {
         return success(dictDataService.selectDictDataById(dictCode));
     }
 
@@ -73,11 +70,9 @@ public class SysDictDataController extends BaseController
      * 根据字典类型查询字典数据信息
      */
     @GetMapping(value = "/type/{dictType}")
-    public AjaxResult dictType(@PathVariable String dictType)
-    {
+    public AjaxResult dictType(@PathVariable String dictType) {
         List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
-        if (StringUtils.isNull(data))
-        {
+        if (StringUtils.isNull(data)) {
             data = new ArrayList<SysDictData>();
         }
         return success(data);
@@ -89,8 +84,7 @@ public class SysDictDataController extends BaseController
     @PreAuthorize("@ss.hasPermi('system:dict:add')")
     @Log(title = "字典数据", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@Validated @RequestBody SysDictData dict)
-    {
+    public AjaxResult add(@Validated @RequestBody SysDictData dict) {
         dict.setCreateBy(getUsername());
         return toAjax(dictDataService.insertDictData(dict));
     }
@@ -101,8 +95,7 @@ public class SysDictDataController extends BaseController
     @PreAuthorize("@ss.hasPermi('system:dict:edit')")
     @Log(title = "字典数据", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@Validated @RequestBody SysDictData dict)
-    {
+    public AjaxResult edit(@Validated @RequestBody SysDictData dict) {
         dict.setUpdateBy(getUsername());
         return toAjax(dictDataService.updateDictData(dict));
     }
@@ -113,8 +106,7 @@ public class SysDictDataController extends BaseController
     @PreAuthorize("@ss.hasPermi('system:dict:remove')")
     @Log(title = "字典类型", businessType = BusinessType.DELETE)
     @DeleteMapping("/{dictCodes}")
-    public AjaxResult remove(@PathVariable Long[] dictCodes)
-    {
+    public AjaxResult remove(@PathVariable Long[] dictCodes) {
         dictDataService.deleteDictDataByIds(dictCodes);
         return success();
     }

+ 13 - 23
nightFragrance-admin/src/main/java/com/ylx/web/controller/system/SysDictTypeController.java

@@ -2,6 +2,7 @@ package com.ylx.web.controller.system;
 
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
@@ -23,21 +24,19 @@ import com.ylx.common.utils.poi.ExcelUtil;
 import com.ylx.system.service.ISysDictTypeService;
 
 /**
- * 数据字典信息
+ * 字典类型
  *
  * @author ylx
  */
 @RestController
 @RequestMapping("/system/dict/type")
-public class SysDictTypeController extends BaseController
-{
+public class SysDictTypeController extends BaseController {
     @Autowired
     private ISysDictTypeService dictTypeService;
 
     @PreAuthorize("@ss.hasPermi('system:dict:list')")
     @GetMapping("/list")
-    public TableDataInfo list(SysDictType dictType)
-    {
+    public TableDataInfo list(SysDictType dictType) {
         startPage();
         List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
         return getDataTable(list);
@@ -46,8 +45,7 @@ public class SysDictTypeController extends BaseController
     @Log(title = "字典类型", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:dict:export')")
     @PostMapping("/export")
-    public void export(HttpServletResponse response, SysDictType dictType)
-    {
+    public void export(HttpServletResponse response, SysDictType dictType) {
         List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
         ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
         util.exportExcel(response, list, "字典类型");
@@ -58,8 +56,7 @@ public class SysDictTypeController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('system:dict:query')")
     @GetMapping(value = "/{dictId}")
-    public AjaxResult getInfo(@PathVariable Long dictId)
-    {
+    public AjaxResult getInfo(@PathVariable Long dictId) {
         return success(dictTypeService.selectDictTypeById(dictId));
     }
 
@@ -69,10 +66,8 @@ public class SysDictTypeController extends BaseController
     @PreAuthorize("@ss.hasPermi('system:dict:add')")
     @Log(title = "字典类型", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@Validated @RequestBody SysDictType dict)
-    {
-        if (!dictTypeService.checkDictTypeUnique(dict))
-        {
+    public AjaxResult add(@Validated @RequestBody SysDictType dict) {
+        if (!dictTypeService.checkDictTypeUnique(dict)) {
             return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
         }
         dict.setCreateBy(getUsername());
@@ -85,10 +80,8 @@ public class SysDictTypeController extends BaseController
     @PreAuthorize("@ss.hasPermi('system:dict:edit')")
     @Log(title = "字典类型", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@Validated @RequestBody SysDictType dict)
-    {
-        if (!dictTypeService.checkDictTypeUnique(dict))
-        {
+    public AjaxResult edit(@Validated @RequestBody SysDictType dict) {
+        if (!dictTypeService.checkDictTypeUnique(dict)) {
             return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
         }
         dict.setUpdateBy(getUsername());
@@ -101,8 +94,7 @@ public class SysDictTypeController extends BaseController
     @PreAuthorize("@ss.hasPermi('system:dict:remove')")
     @Log(title = "字典类型", businessType = BusinessType.DELETE)
     @DeleteMapping("/{dictIds}")
-    public AjaxResult remove(@PathVariable Long[] dictIds)
-    {
+    public AjaxResult remove(@PathVariable Long[] dictIds) {
         dictTypeService.deleteDictTypeByIds(dictIds);
         return success();
     }
@@ -113,8 +105,7 @@ public class SysDictTypeController extends BaseController
     @PreAuthorize("@ss.hasPermi('system:dict:remove')")
     @Log(title = "字典类型", businessType = BusinessType.CLEAN)
     @DeleteMapping("/refreshCache")
-    public AjaxResult refreshCache()
-    {
+    public AjaxResult refreshCache() {
         dictTypeService.resetDictCache();
         return success();
     }
@@ -123,8 +114,7 @@ public class SysDictTypeController extends BaseController
      * 获取字典选择框列表
      */
     @GetMapping("/optionselect")
-    public AjaxResult optionselect()
-    {
+    public AjaxResult optionselect() {
         List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
         return success(dictTypes);
     }

+ 25 - 9
nightFragrance-admin/src/main/resources/application-dev.yml

@@ -56,14 +56,10 @@ spring:
     driverClassName: com.mysql.cj.jdbc.Driver
     druid:
       # 主库数据源
-#      master:
-#        url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-#        username: root
-#        password: 1234
       master:
-        url: jdbc:mysql://39.98.239.48:3366/night_fragrance?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-        username: root
-        password: '@RqGHG#KZ*C9ytkQ'
+        url: jdbc:mysql://rm-d7q5a6qathaxdzhw.mysql.zhangbei.rds.aliyuncs.com:3306/guangyuyuan?serverTimezone=Asia/Shanghai&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
+        username: sxzgkj
+        password: 'Iy$@$koH@oURxyvw'
       slave:
         # 从数据源开关/默认关闭
         enabled: false
@@ -244,8 +240,8 @@ wx:
 wechat:
 #  mpAppId: wxa408092ddcec15b8
 #  mpAppSecret: 3d0953e32e84180b945ace15050365c6
-  mpAppId: wxa72f643173a90106
-  mpAppSecret: 6ceb4647506a4f1654bb10773b227357
+  mpAppId: wx9dc677bd41e82569
+  mpAppSecret: 346ed2d83c0cab7816c73a1df2e04df3
   # 获取code
   get-code-url: https://open.weixin.qq.com/connect/oauth2/authorize
   # 回调地址
@@ -301,3 +297,23 @@ percent:
   0.06
 hPercent:
   0.94
+
+# 高德地图
+amap:
+  apiKey: 5457092e6c62b83c1b2e45dbe973d858
+
+remote:
+  # 用户中心
+  user-center:
+    # 基础URL
+    base-url: https://test.baoxianzhanggui.com/user-api/usercenter
+  # 本地生活
+  local-live:
+    # 基础URL
+    base-url: https://life.baoxianzhanggui.com/locallive-pro-java
+    # base-url: http://192.168.1.190:8082/jeecg-boot
+    #本地生活平台id
+    client-id: bdsh-X9yZ1wV3uT5sR7qP9o
+  # 广誉远
+  night-fragrance:
+    client-id: gyy-aB4cD6eF8gH0iJ2kL4

+ 22 - 2
nightFragrance-admin/src/main/resources/application-pro.yml

@@ -59,7 +59,7 @@ spring:
     druid:
       # 主库数据源
       master:
-        url: jdbc:mysql://rm-d7q5a6qathaxdzhw.mysql.zhangbei.rds.aliyuncs.com:3306/night_fragrance?serverTimezone=Asia/Shanghai&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
+        url: jdbc:mysql://rm-d7q5a6qathaxdzhw.mysql.zhangbei.rds.aliyuncs.com:3306/guangyuyuan?serverTimezone=Asia/Shanghai&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
         username: sxzgkj
         password: 'Iy$@$koH@oURxyvw'
       # 从库数据源
@@ -284,4 +284,24 @@ hCount:
 percent:
   0.06
 hPercent:
-  0.94
+  0.94
+
+# 高德地图
+amap:
+  apiKey: 5457092e6c62b83c1b2e45dbe973d858
+
+remote:
+  # 用户中心
+  user-center:
+    # 基础URL
+    base-url: https://test.baoxianzhanggui.com/user-api/usercenter
+  # 本地生活
+  local-live:
+    # 基础URL
+    base-url: https://life.baoxianzhanggui.com/locallive-pro-java
+    # base-url: http://192.168.1.190:8082/jeecg-boot
+    #本地生活平台id
+    client-id: bdsh-X9yZ1wV3uT5sR7qP9o
+  # 广誉远
+  night-fragrance:
+    client-id: gyy-aB4cD6eF8gH0iJ2kL4

+ 6 - 0
nightFragrance-common/pom.xml

@@ -206,6 +206,12 @@
             <artifactId>dysmsapi20170525</artifactId>
             <version>3.1.0</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>wx-java-pay-spring-boot-starter</artifactId>
+            <version>4.5.0</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 20 - 0
nightFragrance-common/src/main/java/com/ylx/common/component/CustomerAuth.java

@@ -0,0 +1,20 @@
+package com.ylx.common.component;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.ylx.common.utils.SecurityUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * 自定义客户权限校验类
+ */
+@Component("customerAuth")
+public class CustomerAuth {
+
+    private static final Integer CUSTOMER = 0;
+
+    public boolean isCustomer() {
+        Integer role = SecurityUtils.getWxLoginUser().getRole();
+        return ObjectUtil.equals(CUSTOMER, role);
+    }
+
+}

+ 20 - 0
nightFragrance-common/src/main/java/com/ylx/common/component/MerchantAuth.java

@@ -0,0 +1,20 @@
+package com.ylx.common.component;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.ylx.common.utils.SecurityUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * 自定义商家权限校验类
+ */
+@Component("merchantAuth")
+public class MerchantAuth {
+
+    private static final Integer MERCHANT = 1;
+
+    public boolean isMerchant() {
+        Integer role = SecurityUtils.getWxLoginUser().getRole();
+        return ObjectUtil.equals(MERCHANT, role);
+    }
+
+}

+ 82 - 0
nightFragrance-common/src/main/java/com/ylx/common/config/WxPayBeanConfig.java

@@ -0,0 +1,82 @@
+package com.ylx.common.config;
+
+import cn.hutool.core.util.StrUtil;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+@Slf4j
+@Configuration
+public class WxPayBeanConfig {
+
+    @Autowired
+    private WxPayConfig wxPayConfig;
+    @Autowired
+    private ResourceLoader resourceLoader;
+
+    @Bean
+    public WxPayService wxPayService() throws Exception {
+        // SDK原生配置
+        com.github.binarywang.wxpay.config.WxPayConfig sdkConfig = new com.github.binarywang.wxpay.config.WxPayConfig();
+        sdkConfig.setAppId(wxPayConfig.getAppId());
+        sdkConfig.setMchId(wxPayConfig.getMchId());
+        // v3密钥
+        sdkConfig.setApiV3Key(wxPayConfig.getMchKey());
+
+        if (StrUtil.isBlank(wxPayConfig.getCertKeyPath())) {
+            log.warn("wx.cert-key-path未配置,跳过初始化微信支付服务");
+            return null;
+        }
+
+        // 读取私钥文件 apiclient_key.pem
+        Resource keyRes = resourceLoader.getResource(wxPayConfig.getCertKeyPath());
+        if(keyRes.exists()){
+            try (InputStream is = keyRes.getInputStream()) {
+                PrivateKey privateKey = loadPrivateKey(is);
+                sdkConfig.setPrivateKey(privateKey);
+                log.info("商户私钥文件加载成功:{}",wxPayConfig.getCertKeyPath());
+            } catch (Exception e) {
+                log.error("私钥文件存在但读取失败:{}",wxPayConfig.getCertKeyPath(),e);
+            }
+        }else{
+            // 文件不存在,只打日志,不中断启动
+            log.warn("商户私钥文件【{}】不存在,微信支付功能禁用",wxPayConfig.getCertKeyPath());
+        }
+
+        WxPayService payService = new WxPayServiceImpl();
+        payService.setConfig(sdkConfig);
+        return payService;
+    }
+
+    /** 从pem输入流读取RSA私钥 */
+    private PrivateKey loadPrivateKey(InputStream inputStream) throws Exception {
+        try {
+            // JDK8不能用inputStream.readAllBytes(),替换成IOUtils
+            byte[] bytes = IOUtils.toByteArray(inputStream);
+            String pem = new String(bytes)
+                    .replace("-----BEGIN PRIVATE KEY-----", "")
+                    .replace("-----END PRIVATE KEY-----", "")
+                    .replaceAll("\\s+", ""); // \s+ 清除所有换行、空格
+
+            byte[] decode = Base64.getDecoder().decode(pem);
+            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decode);
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            return kf.generatePrivate(spec);
+        } finally {
+            // 关闭流,防止资源泄漏
+            IOUtils.closeQuietly(inputStream);
+        }
+    }
+}

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

@@ -7,7 +7,7 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum SendSmsEnum {
 
-    SMS_220650023("SMS_220650023", "山西掌柜鼎科技", "验证码短信");
+    SMS_220650024("SMS_220650024", "广誉源", "验证码短信");
 
     private final String code;
 

+ 18 - 0
nightFragrance-common/src/main/java/com/ylx/common/weixinPay/enums/WxPayTypeEnum.java

@@ -0,0 +1,18 @@
+package com.ylx.common.weixinPay.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum WxPayTypeEnum {
+
+    GIFT_CARD("GIFT_CARD", "购物卡"),
+    EMOTION_GOODS("EMOTION_GOODS", "情感服务商品");
+
+    private final String code;
+    private final String desc;
+
+    WxPayTypeEnum(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+}

+ 90 - 0
nightFragrance-common/src/main/java/com/ylx/common/weixinPay/service/WxPayV3Service.java

@@ -0,0 +1,90 @@
+package com.ylx.common.weixinPay.service;
+
+import cn.hutool.core.util.StrUtil;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.ylx.common.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class WxPayV3Service {
+
+    @Resource
+    private WxPayService wxPayService;
+
+    /**
+     * V3 JSAPI下单 公众号/小程序
+     */
+    public Map<String, Object> createV3JsapiOrder(String orderNo, BigDecimal amount, String body, String openId, String attach) {
+        try {
+            // 1. 获取支付配置
+            WxPayConfig payConfig = wxPayService.getConfig();
+
+            // 2. 组装支付请求
+            WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
+            request.setOutTradeNo(orderNo);
+            request.setDescription(body);
+            request.setNotifyUrl(payConfig.getNotifyUrl());
+
+            // 设置 attach 字段
+            if (StrUtil.isNotBlank(attach)) {
+                request.setAttach(attach);
+            }
+
+            // 金额转换(元转分,精度校验)
+            int total = amount.multiply(new BigDecimal(100))
+                    .setScale(0, RoundingMode.HALF_UP).intValue();
+            WxPayUnifiedOrderV3Request.Amount amountObj = new WxPayUnifiedOrderV3Request.Amount();
+            amountObj.setTotal(total);
+            request.setAmount(amountObj);
+
+            // 付款人信息
+            WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
+            payer.setOpenid(openId);
+            request.setPayer(payer);
+
+            // 3. 发起支付
+            WxPayUnifiedOrderV3Result result = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, request);
+            log.info("微信支付预支付成功,prepayId: {}", result.getPrepayId());
+
+            // 4. 生成前端JSAPI支付参数
+            WxPayUnifiedOrderV3Result.JsapiResult jsapi = result.getPayInfo(
+                    TradeTypeEnum.JSAPI,
+                    payConfig.getAppId(),
+                    payConfig.getMchId(),
+                    payConfig.getPrivateKey()
+            );
+
+            // 5. 封装返回结果
+            Map<String, Object> payMap = new HashMap<>();
+            payMap.put("appId", jsapi.getAppId());
+            payMap.put("timeStamp", jsapi.getTimeStamp());
+            payMap.put("nonceStr", jsapi.getNonceStr());
+            payMap.put("package", jsapi.getPackageValue());
+            payMap.put("signType", jsapi.getSignType());
+            payMap.put("paySign", jsapi.getPaySign());
+            return payMap;
+
+        } catch (WxPayException e) {
+            // 微信支付业务异常(如签名错误、余额不足)
+            log.error("微信支付业务异常,订单号: {}", orderNo, e);
+            throw new ServiceException("支付失败:" + e.getMessage());
+        } catch (Exception e) {
+            // 系统级异常(如网络超时、配置错误)
+            log.error("微信支付系统异常,订单号: {}", orderNo, e);
+            throw new ServiceException("支付服务异常,请稍后重试");
+        }
+    }
+}

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

@@ -114,7 +114,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
                         "/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()
+                        "/userCenter/userApp/bind/update","/serviceCategory/h5List","/api/lbt/v1/select").permitAll()
                 // 静态资源,可匿名访问
                 .antMatchers(HttpMethod.GET, "/", "/*.txt", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()

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

@@ -75,9 +75,9 @@ public class WxTokenService {
                 String userKey = getTokenKey(uuid);
                 log.info("userKey: {}", userKey);
                 //测试环境使用缓存 WxLoginUser的信息直接返回
-                if(DEV.equals(env)) {
+                /*if(DEV.equals(env)) {
                     userKey = "tf:e9df2769-5626-45f0-97f9-05a158fc0a6d";
-                }
+                }*/
                 WxLoginUser user = redisCache.getCacheObject(userKey);
                 log.info("user对象的值:{}", JSON.toJSONString(user));
                 return user;

+ 4 - 1
nightFragrance-massage/pom.xml

@@ -53,6 +53,9 @@
             <groupId>com.ruoyi</groupId>
             <artifactId>nightFragrance-framework</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>com.github.stuxuhai</groupId>
+            <artifactId>jpinyin</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 115 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/controller/AttendanceConfigController.java

@@ -0,0 +1,115 @@
+package com.ylx.attendanceconfig.controller;
+
+import com.ylx.attendanceconfig.domain.dto.AttendanceRuleAddDTO;
+import com.ylx.attendanceconfig.domain.vo.AttendanceRuleDetailVO;
+import com.ylx.attendanceconfig.service.AttendanceConfigService;
+import com.ylx.common.annotation.Log;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.enums.BusinessType;
+import com.ylx.common.exception.ServiceException;
+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.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+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;
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/attendance/config")
+@Api(tags = {"考勤规则配置"})
+public class AttendanceConfigController {
+
+    @Resource
+    private AttendanceConfigService attendanceConfigService;
+
+    /**
+     * 新增考勤规则
+     *
+     * @param dto 考勤规则参数
+     * @return 操作结果
+     */
+    @PostMapping
+    @ApiOperation("新增考勤规则")
+    @Log(title = "新增考勤规则", businessType = BusinessType.INSERT)
+    public R<?> add(@Validated @RequestBody AttendanceRuleAddDTO dto) {
+        try {
+            attendanceConfigService.addAttendanceRule(dto);
+            return R.ok();
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("新增考勤规则异常", e);
+            return R.fail("新增考勤规则失败");
+        }
+    }
+
+    /**
+     * 查询全部考勤规则
+     *
+     * @return R<List<AttendanceRuleDetailVO>> 考勤规则详情集合
+     */
+    @GetMapping
+    @ApiOperation("查询全部考勤规则")
+    public R<List<AttendanceRuleDetailVO>> query() {
+        try {
+            List<AttendanceRuleDetailVO> list = attendanceConfigService.listAttendanceRules();
+            return R.ok(list);
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("查询全部考勤规则异常", e);
+            return R.fail("查询全部考勤规则失败");
+        }
+    }
+
+    /**
+     * 删除考勤规则
+     *
+     * @param id 考勤规则ID
+     * @return 操作结果
+     */
+    @DeleteMapping("/{id}")
+    @ApiOperation("删除考勤规则")
+    @Log(title = "删除考勤规则", businessType = BusinessType.DELETE)
+    public R<?> delete(@PathVariable("id") Long id) {
+        try {
+            attendanceConfigService.deleteAttendanceRule(id);
+            return R.ok();
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("删除考勤规则异常", e);
+            return R.fail("删除考勤规则失败");
+        }
+    }
+
+    /**
+     * 删除考勤扣款区间
+     *
+     * @param id 扣款区间ID
+     * @return 操作结果
+     */
+    @DeleteMapping("/deduction/{id}")
+    @ApiOperation("删除考勤扣款区间")
+    @Log(title = "删除考勤扣款区间", businessType = BusinessType.DELETE)
+    public R<?> deleteDeduction(@PathVariable("id") Long id) {
+        try {
+            attendanceConfigService.deleteDeductionRule(id);
+            return R.ok();
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("删除考勤扣款区间异常", e);
+            return R.fail("删除考勤扣款区间失败");
+        }
+    }
+}

+ 110 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/AttendanceDeductionRule.java

@@ -0,0 +1,110 @@
+package com.ylx.attendanceconfig.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+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 io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 考勤扣款区间规则表
+ */
+@Data
+@ApiModel("考勤扣款区间规则表")
+@TableName("attendance_deduction_rule")
+public class AttendanceDeductionRule implements Serializable {
+    private static final long serialVersionUID = -3851089045114907220L;
+
+    /**
+     * 主键ID
+     */
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 考勤规则ID,关联attendance_rule.id
+     */
+    @ApiModelProperty("考勤规则ID,关联attendance_rule.id")
+    @TableField("rule_id")
+    private Long ruleId;
+
+    /**
+     * 规则类型:1=早退时长扣款
+     */
+    @ApiModelProperty("规则类型:1=早退时长扣款")
+    @TableField("rule_type")
+    private Integer ruleType;
+
+    /**
+     * 开始分钟数,包含该值
+     */
+    @ApiModelProperty("开始分钟数,包含该值")
+    @TableField("start_minutes")
+    private Integer startMinutes;
+
+    /**
+     * 结束分钟数,包含该值
+     */
+    @ApiModelProperty("结束分钟数,包含该值")
+    @TableField("end_minutes")
+    private Integer endMinutes;
+
+    /**
+     * 扣款金额,单位:元
+     */
+    @ApiModelProperty("扣款金额,单位:元")
+    @TableField("deduct_amount")
+    private BigDecimal deductAmount;
+
+    /**
+     * 排序
+     */
+    @ApiModelProperty("排序")
+    @TableField("sort_order")
+    private Integer sortOrder;
+
+    /**
+     * 创建人
+     */
+    @ApiModelProperty("创建人")
+    @TableField("create_by")
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    @ApiModelProperty("创建时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 更新人
+     */
+    @ApiModelProperty("更新人")
+    @TableField("update_by")
+    private String updateBy;
+
+    /**
+     * 更新时间
+     */
+    @ApiModelProperty("更新时间")
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 是否删除:0=否,1=是
+     */
+    @ApiModelProperty("是否删除:0=否,1=是")
+    @TableField("is_delete")
+    @TableLogic
+    private Integer isDelete;
+}

+ 103 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/AttendanceRule.java

@@ -0,0 +1,103 @@
+package com.ylx.attendanceconfig.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+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 io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 考勤规则主表
+ */
+@Data
+@ApiModel("考勤规则主表")
+@TableName("attendance_rule")
+public class AttendanceRule implements Serializable {
+    private static final long serialVersionUID = 2048164226986084497L;
+
+    /**
+     * 主键ID
+     */
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 规则名称
+     */
+    @ApiModelProperty("规则名称")
+    @TableField("rule_name")
+    private String ruleName;
+
+    /**
+     * 商户ID,为空表示平台默认规则
+     */
+    @ApiModelProperty("商户ID,为空表示平台默认规则")
+    @TableField("merchant_id")
+    private String merchantId;
+
+    /**
+     * 基本工作时长,单位:小时
+     */
+    @ApiModelProperty("基本工作时长,单位:小时")
+    @TableField("basic_work_hours")
+    private BigDecimal basicWorkHours;
+
+    /**
+     * 是否启用工作时长规则:0=无,1=有
+     */
+    @ApiModelProperty("是否启用工作时长规则:0=无,1=有")
+    @TableField("work_duration_rule_enabled")
+    private Integer workDurationRuleEnabled;
+
+    /**
+     * 状态:0=停用,1=启用
+     */
+    @ApiModelProperty("状态:0=停用,1=启用")
+    @TableField("status")
+    private Integer status;
+
+    /**
+     * 创建人
+     */
+    @ApiModelProperty("创建人")
+    @TableField("create_by")
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    @ApiModelProperty("创建时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 更新人
+     */
+    @ApiModelProperty("更新人")
+    @TableField("update_by")
+    private String updateBy;
+
+    /**
+     * 更新时间
+     */
+    @ApiModelProperty("更新时间")
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 是否删除:0=否,1=是
+     */
+    @ApiModelProperty("是否删除:0=否,1=是")
+    @TableField("is_delete")
+    @TableLogic
+    private Integer isDelete;
+}

+ 38 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/dto/AttendanceDeductionRuleDTO.java

@@ -0,0 +1,38 @@
+package com.ylx.attendanceconfig.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("考勤扣款区间DTO")
+public class AttendanceDeductionRuleDTO implements Serializable {
+    private static final long serialVersionUID = -8789319415530497294L;
+
+    @ApiModelProperty("扣款区间ID,新增时可不传")
+    private Long id;
+
+    @ApiModelProperty("规则类型:1=早退时长扣款,不传默认1")
+    private Integer ruleType;
+
+    @NotNull(message = "开始分钟数不能为空")
+    @Min(value = 1, message = "开始分钟数必须大于0")
+    @ApiModelProperty(value = "开始分钟数,包含该值", required = true)
+    private Integer startMinutes;
+
+    @NotNull(message = "结束分钟数不能为空")
+    @Min(value = 1, message = "结束分钟数必须大于0")
+    @ApiModelProperty(value = "结束分钟数,包含该值", required = true)
+    private Integer endMinutes;
+
+    @NotNull(message = "扣款金额不能为空")
+    @DecimalMin(value = "0", message = "扣款金额不能小于0")
+    @ApiModelProperty(value = "扣款金额,单位:元", required = true)
+    private BigDecimal deductAmount;
+}

+ 49 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/dto/AttendanceRuleAddDTO.java

@@ -0,0 +1,49 @@
+package com.ylx.attendanceconfig.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("考勤规则新增DTO")
+public class AttendanceRuleAddDTO implements Serializable {
+    private static final long serialVersionUID = 1163485126326629827L;
+
+    @ApiModelProperty("规则名称,不传默认:默认考勤规则")
+    private String ruleName;
+
+    @ApiModelProperty("商户ID,为空表示平台默认规则")
+    private String merchantId;
+
+    @NotNull(message = "基本工作时长不能为空")
+    @DecimalMin(value = "0.01", message = "基本工作时长必须大于0")
+    @ApiModelProperty(value = "基本工作时长,单位:小时", required = true)
+    private BigDecimal basicWorkHours;
+
+    /**
+     * 是否启用工作时长规则:0=无,1=有
+     */
+    @NotNull(message = "工作时长规则不能为空")
+    @ApiModelProperty(value = "是否启用工作时长规则:0=无,1=有", required = true)
+    private Integer workDurationRuleEnabled;
+
+    /**
+     * 状态:0=停用,1=启用
+     */
+    @ApiModelProperty("状态:0=停用,1=启用,不传默认启用")
+    private Integer status;
+
+    /**
+     * 扣款区间规则集合
+     */
+    @Valid
+    @ApiModelProperty("扣款区间规则集合")
+    private List<AttendanceDeductionRuleDTO> deductionRules;
+}

+ 32 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/vo/AttendanceDeductionRuleVO.java

@@ -0,0 +1,32 @@
+package com.ylx.attendanceconfig.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("考勤扣款区间VO")
+public class AttendanceDeductionRuleVO implements Serializable {
+    private static final long serialVersionUID = -5881577398957998820L;
+
+    @ApiModelProperty("扣款区间ID")
+    private Long id;
+
+    @ApiModelProperty("规则类型:1=早退时长扣款")
+    private Integer ruleType;
+
+    @ApiModelProperty("开始分钟数,包含该值")
+    private Integer startMinutes;
+
+    @ApiModelProperty("结束分钟数,包含该值")
+    private Integer endMinutes;
+
+    @ApiModelProperty("扣款金额,单位:元")
+    private BigDecimal deductAmount;
+
+    @ApiModelProperty("排序")
+    private Integer sortOrder;
+}

+ 46 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/vo/AttendanceRuleDetailVO.java

@@ -0,0 +1,46 @@
+package com.ylx.attendanceconfig.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@ApiModel("考勤规则详情VO")
+public class AttendanceRuleDetailVO implements Serializable {
+    private static final long serialVersionUID = -7242343396476539324L;
+
+    @ApiModelProperty("考勤规则ID")
+    private Long id;
+
+    @ApiModelProperty("规则名称")
+    private String ruleName;
+
+    @ApiModelProperty("商户ID,为空表示平台默认规则")
+    private String merchantId;
+
+    @ApiModelProperty("基本工作时长,单位:小时")
+    private BigDecimal basicWorkHours;
+
+    @ApiModelProperty("是否启用工作时长规则:0=无,1=有")
+    private Integer workDurationRuleEnabled;
+
+    @ApiModelProperty("状态:0=停用,1=启用")
+    private Integer status;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("扣款区间规则集合")
+    private List<AttendanceDeductionRuleVO> deductionRules;
+}

+ 7 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/mapper/AttendanceDeductionRuleMapper.java

@@ -0,0 +1,7 @@
+package com.ylx.attendanceconfig.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.attendanceconfig.domain.AttendanceDeductionRule;
+
+public interface AttendanceDeductionRuleMapper extends BaseMapper<AttendanceDeductionRule> {
+}

+ 7 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/mapper/AttendanceRuleMapper.java

@@ -0,0 +1,7 @@
+package com.ylx.attendanceconfig.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.attendanceconfig.domain.AttendanceRule;
+
+public interface AttendanceRuleMapper extends BaseMapper<AttendanceRule> {
+}

+ 17 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/service/AttendanceConfigService.java

@@ -0,0 +1,17 @@
+package com.ylx.attendanceconfig.service;
+
+import com.ylx.attendanceconfig.domain.dto.AttendanceRuleAddDTO;
+import com.ylx.attendanceconfig.domain.vo.AttendanceRuleDetailVO;
+
+import java.util.List;
+
+public interface AttendanceConfigService {
+
+    void addAttendanceRule(AttendanceRuleAddDTO dto);
+
+    List<AttendanceRuleDetailVO> listAttendanceRules();
+
+    void deleteAttendanceRule(Long id);
+
+    void deleteDeductionRule(Long id);
+}

+ 220 - 0
nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/service/impl/AttendanceConfigServiceImpl.java

@@ -0,0 +1,220 @@
+package com.ylx.attendanceconfig.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+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.ylx.attendanceconfig.domain.AttendanceDeductionRule;
+import com.ylx.attendanceconfig.domain.AttendanceRule;
+import com.ylx.attendanceconfig.domain.dto.AttendanceDeductionRuleDTO;
+import com.ylx.attendanceconfig.domain.dto.AttendanceRuleAddDTO;
+import com.ylx.attendanceconfig.domain.vo.AttendanceDeductionRuleVO;
+import com.ylx.attendanceconfig.domain.vo.AttendanceRuleDetailVO;
+import com.ylx.attendanceconfig.mapper.AttendanceDeductionRuleMapper;
+import com.ylx.attendanceconfig.mapper.AttendanceRuleMapper;
+import com.ylx.attendanceconfig.service.AttendanceConfigService;
+import com.ylx.common.exception.ServiceException;
+import com.ylx.common.utils.SecurityUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class AttendanceConfigServiceImpl implements AttendanceConfigService {
+
+    @Resource
+    private AttendanceRuleMapper attendanceRuleMapper;
+
+    @Resource
+    private AttendanceDeductionRuleMapper attendanceDeductionRuleMapper;
+
+    private static final int NOT_DELETE = 0;
+    private static final int DELETE = 1;
+    private static final int ENABLED = 1;
+    private static final int DISABLED = 0;
+    private static final int EARLY_LEAVE_DEDUCTION = 1;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void addAttendanceRule(AttendanceRuleAddDTO dto) {
+        checkRuleParam(dto);
+
+        AttendanceRule rule = new AttendanceRule();
+        rule.setRuleName(StrUtil.blankToDefault(StrUtil.trim(dto.getRuleName()), "默认考勤规则"));
+        rule.setMerchantId(StrUtil.trim(dto.getMerchantId()));
+        rule.setBasicWorkHours(dto.getBasicWorkHours());
+        rule.setWorkDurationRuleEnabled(dto.getWorkDurationRuleEnabled());
+        rule.setStatus(ObjectUtil.defaultIfNull(dto.getStatus(), ENABLED));
+        rule.setIsDelete(NOT_DELETE);
+        rule.setCreateBy(SecurityUtils.getUsername());
+        rule.setUpdateBy(SecurityUtils.getUsername());
+
+        int insertRuleResult = attendanceRuleMapper.insert(rule);
+        if (insertRuleResult <= 0 || ObjectUtil.isNull(rule.getId())) {
+            throw new ServiceException("新增考勤规则失败");
+        }
+
+        if (dto.getWorkDurationRuleEnabled() == ENABLED) {
+            List<AttendanceDeductionRuleDTO> sortedRules = sortDeductionRules(dto.getDeductionRules());
+            for (int i = 0; i < sortedRules.size(); i++) {
+                AttendanceDeductionRuleDTO deductionDto = sortedRules.get(i);
+                AttendanceDeductionRule deductionRule = new AttendanceDeductionRule();
+                deductionRule.setRuleId(rule.getId());
+                deductionRule.setRuleType(ObjectUtil.defaultIfNull(deductionDto.getRuleType(), EARLY_LEAVE_DEDUCTION));
+                deductionRule.setStartMinutes(deductionDto.getStartMinutes());
+                deductionRule.setEndMinutes(deductionDto.getEndMinutes());
+                deductionRule.setDeductAmount(deductionDto.getDeductAmount());
+                deductionRule.setSortOrder(i + 1);
+                deductionRule.setIsDelete(NOT_DELETE);
+                deductionRule.setCreateBy(SecurityUtils.getUsername());
+                deductionRule.setUpdateBy(SecurityUtils.getUsername());
+
+                int insertDeductionResult = attendanceDeductionRuleMapper.insert(deductionRule);
+                if (insertDeductionResult <= 0) {
+                    throw new ServiceException("新增考勤扣款区间失败");
+                }
+            }
+        }
+    }
+
+    @Override
+    public List<AttendanceRuleDetailVO> listAttendanceRules() {
+        List<AttendanceRule> ruleList = attendanceRuleMapper.selectList(new LambdaQueryWrapper<AttendanceRule>()
+                .orderByDesc(AttendanceRule::getCreateTime));
+        return ruleList.stream()
+                .map(this::toDetailVO)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteAttendanceRule(Long id) {
+        if (ObjectUtil.isNull(id) || id <= 0) {
+            throw new ServiceException("考勤规则ID不正确");
+        }
+        AttendanceRule rule = attendanceRuleMapper.selectOne(new LambdaQueryWrapper<AttendanceRule>()
+                .eq(AttendanceRule::getId, id)
+                .eq(AttendanceRule::getIsDelete, NOT_DELETE));
+        if (ObjectUtil.isNull(rule)) {
+            throw new ServiceException("考勤规则不存在或已删除");
+        }
+
+        String username = SecurityUtils.getUsername();
+        LocalDateTime now = LocalDateTime.now();
+        int updateRuleResult = attendanceRuleMapper.update(null, new LambdaUpdateWrapper<AttendanceRule>()
+                .eq(AttendanceRule::getId, id)
+                .eq(AttendanceRule::getIsDelete, NOT_DELETE)
+                .set(AttendanceRule::getIsDelete, DELETE)
+                .set(AttendanceRule::getUpdateBy, username)
+                .set(AttendanceRule::getUpdateTime, now));
+        if (updateRuleResult <= 0) {
+            throw new ServiceException("删除考勤规则失败");
+        }
+
+        attendanceDeductionRuleMapper.update(null, new LambdaUpdateWrapper<AttendanceDeductionRule>()
+                .eq(AttendanceDeductionRule::getRuleId, id)
+                .eq(AttendanceDeductionRule::getIsDelete, NOT_DELETE)
+                .set(AttendanceDeductionRule::getIsDelete, DELETE)
+                .set(AttendanceDeductionRule::getUpdateBy, username)
+                .set(AttendanceDeductionRule::getUpdateTime, now));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteDeductionRule(Long id) {
+        if (ObjectUtil.isNull(id) || id <= 0) {
+            throw new ServiceException("扣款区间ID不正确");
+        }
+        AttendanceDeductionRule deductionRule = attendanceDeductionRuleMapper.selectOne(
+                new LambdaQueryWrapper<AttendanceDeductionRule>()
+                        .eq(AttendanceDeductionRule::getId, id));
+        if (ObjectUtil.isNull(deductionRule)) {
+            throw new ServiceException("扣款区间不存在或已删除");
+        }
+
+        int updateResult = attendanceDeductionRuleMapper.deleteById(id);
+        if (updateResult <= 0) {
+            throw new ServiceException("删除扣款区间失败");
+        }
+    }
+
+    private void checkRuleParam(AttendanceRuleAddDTO dto) {
+        if (ObjectUtil.isNull(dto)) {
+            throw new ServiceException("考勤规则参数不能为空");
+        }
+        if (ObjectUtil.isNull(dto.getBasicWorkHours()) || dto.getBasicWorkHours().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("基本工作时长必须大于0");
+        }
+        if (dto.getWorkDurationRuleEnabled() != ENABLED && dto.getWorkDurationRuleEnabled() != DISABLED) {
+            throw new ServiceException("工作时长规则值不正确");
+        }
+        if (ObjectUtil.isNotNull(dto.getStatus()) && dto.getStatus() != ENABLED && dto.getStatus() != DISABLED) {
+            throw new ServiceException("考勤规则状态值不正确");
+        }
+        if (dto.getWorkDurationRuleEnabled() == ENABLED) {
+            checkDeductionRules(dto.getDeductionRules());
+        }
+    }
+
+    private void checkDeductionRules(List<AttendanceDeductionRuleDTO> deductionRules) {
+        if (CollectionUtil.isEmpty(deductionRules)) {
+            throw new ServiceException("启用工作时长规则时,扣款区间不能为空");
+        }
+        List<AttendanceDeductionRuleDTO> sortedRules = sortDeductionRules(deductionRules);
+        int expectedStartMinutes = 1;
+        for (AttendanceDeductionRuleDTO deductionRule : sortedRules) {
+            if (ObjectUtil.defaultIfNull(deductionRule.getRuleType(), EARLY_LEAVE_DEDUCTION) != EARLY_LEAVE_DEDUCTION) {
+                throw new ServiceException("扣款规则类型不正确");
+            }
+            if (ObjectUtil.isNull(deductionRule.getStartMinutes()) || deductionRule.getStartMinutes() <= 0) {
+                throw new ServiceException("开始分钟数必须大于0");
+            }
+            if (ObjectUtil.isNull(deductionRule.getEndMinutes()) || deductionRule.getEndMinutes() < deductionRule.getStartMinutes()) {
+                throw new ServiceException("结束分钟数不能小于开始分钟数");
+            }
+            if (ObjectUtil.isNull(deductionRule.getDeductAmount()) || deductionRule.getDeductAmount().compareTo(BigDecimal.ZERO) < 0) {
+                throw new ServiceException("扣款金额不能小于0");
+            }
+            if (deductionRule.getStartMinutes() != expectedStartMinutes) {
+                throw new ServiceException("扣款区间必须从1分钟开始且连续");
+            }
+            expectedStartMinutes = deductionRule.getEndMinutes() + 1;
+        }
+    }
+
+    private List<AttendanceDeductionRuleDTO> sortDeductionRules(List<AttendanceDeductionRuleDTO> deductionRules) {
+        return deductionRules.stream()
+                .sorted(Comparator.comparing(AttendanceDeductionRuleDTO::getStartMinutes))
+                .collect(Collectors.toList());
+    }
+
+    private AttendanceRuleDetailVO toDetailVO(AttendanceRule rule) {
+        AttendanceRuleDetailVO vo = new AttendanceRuleDetailVO();
+        BeanUtil.copyProperties(rule, vo);
+        vo.setDeductionRules(getDeductionRuleVOList(rule.getId()));
+        return vo;
+    }
+
+    private List<AttendanceDeductionRuleVO> getDeductionRuleVOList(Long ruleId) {
+        List<AttendanceDeductionRule> deductionRules = attendanceDeductionRuleMapper.selectList(
+                new LambdaQueryWrapper<AttendanceDeductionRule>()
+                        .eq(AttendanceDeductionRule::getRuleId, ruleId)
+                        .orderByAsc(AttendanceDeductionRule::getSortOrder)
+                        .orderByAsc(AttendanceDeductionRule::getStartMinutes));
+        return deductionRules.stream().map(entity -> {
+            AttendanceDeductionRuleVO vo = new AttendanceDeductionRuleVO();
+            BeanUtil.copyProperties(entity, vo);
+            return vo;
+        }).collect(Collectors.toList());
+    }
+}

+ 49 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/controller/GiftCardController.java

@@ -0,0 +1,49 @@
+package com.ylx.giftCard.controller;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.core.domain.R;
+import com.ylx.giftCard.domain.GiftCard;
+import com.ylx.giftCard.domain.dto.GiftCardPurchaseDTO;
+import com.ylx.giftCard.domain.vo.GiftCardDetailVO;
+import com.ylx.giftCard.domain.vo.GiftCardVO;
+import com.ylx.giftCard.service.IGiftCardService;
+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;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/gift/card")
+@Api(tags = {"购物卡"})
+@Slf4j
+public class GiftCardController {
+
+    @Resource
+    private IGiftCardService giftCardService;
+
+    @GetMapping("/page")
+    @ApiOperation("购物卡分页列表接口")
+    public R<Page<GiftCardVO>> getGiftCardPage(Page<GiftCard> page) {
+        Page<GiftCardVO> pageData = giftCardService.getGiftCardPage(page);
+        return R.ok(pageData);
+    }
+
+    @PostMapping("/purchase")
+    @ApiOperation("购物卡购买接口")
+    public R<Map<String, Object>> purchaseGiftCard(@Validated @RequestBody GiftCardPurchaseDTO dto) {
+        Map<String, Object> data =giftCardService.purchaseGiftCard(dto);
+        return R.ok(data);
+    }
+
+    @GetMapping("/{id}/detail")
+    @ApiOperation("获取购物卡详情")
+    public R<GiftCardDetailVO> getGiftCardDetail(@PathVariable Long id) {
+        GiftCardDetailVO detail = giftCardService.getGiftCardDetail(id);
+        return ObjectUtil.isNotNull(detail) ? R.ok(detail) : R.fail("购物卡不存在或已被删除");
+    }
+}

+ 177 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/controller/GiftCardManageController.java

@@ -0,0 +1,177 @@
+package com.ylx.giftCard.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.annotation.Log;
+import com.ylx.common.core.domain.R;
+import com.ylx.common.enums.BusinessType;
+import com.ylx.common.exception.ServiceException;
+import com.ylx.giftCard.domain.GiftCard;
+import com.ylx.giftCard.domain.dto.GiftCardManageQueryDTO;
+import com.ylx.giftCard.domain.dto.GiftCardManageSaveDTO;
+import com.ylx.giftCard.domain.dto.GiftCardManageUpdateDTO;
+import com.ylx.giftCard.domain.dto.GiftCardPublishStatusDTO;
+import com.ylx.giftCard.domain.vo.GiftCardManageDetailVO;
+import com.ylx.giftCard.domain.vo.GiftCardManageExportVO;
+import com.ylx.giftCard.domain.vo.GiftCardManagePageVO;
+import com.ylx.giftCard.service.IGiftCardService;
+import com.ylx.common.utils.poi.ExcelUtil;
+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.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+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;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/gift/card/manage")
+@Api(tags = {"购物卡管理"})
+public class GiftCardManageController {
+
+    @Resource
+    private IGiftCardService giftCardService;
+
+    /**
+     * 分页查询购物卡
+     * @param page
+     * @param dto
+     * @return R<Page<GiftCardManagePageVO>>
+     */
+    @GetMapping("/page")
+    @ApiOperation("分页查询购物卡")
+    public R<Page<GiftCardManagePageVO>> page(Page<GiftCard> page, GiftCardManageQueryDTO dto) {
+        try {
+            return R.ok(giftCardService.getManagePage(page, dto));
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("分页查询购物卡异常", e);
+            return R.fail("分页查询购物卡失败");
+        }
+    }
+
+    /**
+     * 导出购物卡
+     * @param response
+     * @param dto
+     */
+    @PostMapping("/export")
+    @ApiOperation("导出购物卡")
+    @Log(title = "导出购物卡", businessType = BusinessType.EXPORT)
+    public void export(HttpServletResponse response, GiftCardManageQueryDTO dto) {
+        try {
+            List<GiftCardManageExportVO> list = giftCardService.getManageExportList(dto);
+            ExcelUtil<GiftCardManageExportVO> util = new ExcelUtil<>(GiftCardManageExportVO.class);
+            util.exportExcel(response, list, "购物卡管理");
+        } catch (ServiceException e) {
+            log.error("导出购物卡参数异常", e);
+            throw e;
+        } catch (Exception e) {
+            log.error("导出购物卡异常", e);
+            throw new RuntimeException("导出购物卡失败", e);
+        }
+    }
+
+    /**
+     * 查询购物卡详情
+     * @param id
+     * @return R<GiftCardManageDetailVO>
+     */
+    @GetMapping("/{id}")
+    @ApiOperation("查询购物卡详情")
+    public R<GiftCardManageDetailVO> detail(@PathVariable("id") Long id) {
+        try {
+            return R.ok(giftCardService.getManageDetail(id));
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("查询购物卡详情异常", e);
+            return R.fail("查询购物卡详情失败");
+        }
+    }
+
+    /**
+     * 新增购物卡
+     * @param dto
+     * @return R<?>
+     */
+    @PostMapping
+    @ApiOperation("新增购物卡")
+    @Log(title = "新增购物卡", businessType = BusinessType.INSERT)
+    public R<?> add(@Validated @RequestBody GiftCardManageSaveDTO dto) {
+        try {
+            giftCardService.addGiftCard(dto);
+            return R.ok();
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("新增购物卡异常", e);
+            return R.fail("新增购物卡失败");
+        }
+    }
+
+    /**
+     * 编辑购物卡
+     * @param dto
+     * @return R<?>
+     */
+    @PutMapping
+    @ApiOperation("编辑购物卡")
+    @Log(title = "编辑购物卡", businessType = BusinessType.UPDATE)
+    public R<?> edit(@Validated @RequestBody GiftCardManageUpdateDTO dto) {
+        try {
+            giftCardService.updateGiftCard(dto);
+            return R.ok();
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("编辑购物卡异常", e);
+            return R.fail("编辑购物卡失败");
+        }
+    }
+
+    @PutMapping("/publish")
+    @ApiOperation("修改购物卡上架状态")
+    @Log(title = "修改购物卡上架状态", businessType = BusinessType.UPDATE)
+    public R<?> updatePublishStatus(@Validated @RequestBody GiftCardPublishStatusDTO dto) {
+        try {
+            giftCardService.updatePublishStatus(dto);
+            return R.ok();
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("修改购物卡上架状态异常", e);
+            return R.fail("修改购物卡上架状态失败");
+        }
+    }
+
+    /**
+     * 删除购物卡
+     * @param id
+     * @return R<?>
+     */
+    @DeleteMapping("/{id}")
+    @ApiOperation("删除购物卡")
+    @Log(title = "删除购物卡", businessType = BusinessType.DELETE)
+    public R<?> delete(@PathVariable("id") Long id) {
+        try {
+            giftCardService.deleteGiftCard(id);
+            return R.ok();
+        } catch (ServiceException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("删除购物卡异常", e);
+            return R.fail("删除购物卡失败");
+        }
+    }
+}

+ 13 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/controller/GiftCardOrderController.java

@@ -0,0 +1,13 @@
+package com.ylx.giftCard.controller;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/gift/card/order")
+@Api(tags = {"购物卡订单"})
+@Slf4j
+public class GiftCardOrderController {
+}

+ 90 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/GiftCard.java

@@ -0,0 +1,90 @@
+package com.ylx.giftCard.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ylx.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 购物卡信息表
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName("gift_card")
+public class GiftCard extends BaseEntity {
+    private static final long serialVersionUID = 2378338959829222898L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 商户ID
+     */
+    private String merchantId;
+
+    /**
+     * 购物卡名称,如“200元购物卡”
+     */
+    private String name;
+
+    /**
+     * 缩略图URL(上传后存储路径)
+     */
+    private String imageUrl;
+
+    /**
+     * 面值金额,单位:元
+     */
+    private BigDecimal amount;
+
+    /**
+     * 商户提成比例,如 1.00 表示 1%
+     */
+    private BigDecimal commissionRate;
+
+    /**
+     * 是否上架:1=上架,0=下架
+     */
+    private Integer isPublished;
+
+    /**
+     * 库存数量,0表示无库存
+     */
+    private Integer stock;
+
+    /**
+     * 累计销量
+     */
+    private Integer sales;
+
+    /**
+     * 有效期开始日期
+     */
+    private LocalDate validStartDate;
+
+    /**
+     * 有效期结束日期
+     */
+    private LocalDate validEndDate;
+
+    /**
+     * 详情(富文本,可存HTML或Markdown)
+     */
+    private String description;
+
+    /**
+     * 是否删除:0=否,1=是。
+     */
+    @TableLogic
+    private Integer isDelete;
+}

+ 105 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/GiftCardOrder.java

@@ -0,0 +1,105 @@
+package com.ylx.giftCard.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.ylx.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 购物卡订单表
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class GiftCardOrder extends BaseEntity {
+    private static final long serialVersionUID = -5890473596508557032L;
+
+    /**
+     * 订单主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 订单编号(如 GC202601041400001)
+     */
+    private String orderNo;
+
+    /**
+     * 关联的购物卡ID
+     */
+    private Long giftCardId;
+
+    /**
+     * 购物卡名称(冗余,防卡被删后信息丢失)
+     */
+    private String giftCardName;
+
+    /**
+     * 购物卡面值(冗余)
+     */
+    private BigDecimal giftCardAmount;
+
+    /**
+     * 购买数量
+     */
+    private Integer purchaseQuantity;
+
+    /**
+     * 下单时的提成比例(冗余,防卡后续修改影响历史订单)
+     */
+    private BigDecimal commissionRate;
+
+    /**
+     * 用户ID(如 uid_xxx)
+     */
+    private String userId;
+
+    /**
+     * 用户姓名/昵称
+     */
+    private String userName;
+
+    /**
+     * 用户手机号
+     */
+    private String userPhone;
+
+    /**
+     * 商户ID
+     */
+    private String merchantId;
+
+    /**
+     * 商户姓名
+     */
+    private String merchantNickName;
+
+    /**
+     * 商户名称
+     */
+    private String merchantName;
+
+    /**
+     * 商户收款账号
+     */
+    private String merchantAccount;
+
+    /**
+     * 用户实付金额(= gift_card_amount)
+     */
+    private BigDecimal payAmount;
+
+    /**
+     * 商户实际提成金额(= pay_amount * commission_rate / 100)
+     */
+    private BigDecimal commissionAmount;
+
+    /**
+     * 订单状态:0=待支付,1=已支付,2=已退款,3=已过期
+     */
+    private Integer status;
+
+}

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

@@ -0,0 +1,29 @@
+package com.ylx.giftCard.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+
+@Data
+@ApiModel("购物卡管理分页查询DTO")
+public class GiftCardManageQueryDTO implements Serializable {
+    private static final long serialVersionUID = 1184559947927043323L;
+
+    @ApiModelProperty("购物卡名称")
+    private String name;
+
+    @ApiModelProperty("创建开始日期,格式:yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate beginCreateDate;
+
+    @ApiModelProperty("创建结束日期,格式:yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endCreateDate;
+
+    @ApiModelProperty("是否上架:1=上架,0=下架")
+    private Integer isPublished;
+}

+ 93 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageSaveDTO.java

@@ -0,0 +1,93 @@
+package com.ylx.giftCard.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+@ApiModel("购物卡管理新增DTO")
+public class GiftCardManageSaveDTO implements Serializable {
+    private static final long serialVersionUID = -3120530382569245494L;
+
+    /**
+     * 商户ID
+     */
+    @ApiModelProperty("商户ID")
+    private String merchantId;
+
+    /**
+     * 购物卡名称
+     */
+    @NotBlank(message = "购物卡名称不能为空")
+    @Size(max = 20, message = "购物卡名称不能超过20个字符")
+    @ApiModelProperty(value = "购物卡名称", required = true)
+    private String name;
+
+    /**
+     * 缩略图URL(上传后存储路径)
+     */
+    @NotBlank(message = "图片不能为空")
+    @ApiModelProperty(value = "图片URL", required = true)
+    private String imageUrl;
+
+    /**
+     * 购物卡金额
+     */
+    @NotNull(message = "购物卡金额不能为空")
+    @DecimalMin(value = "0.01", message = "购物卡金额必须大于0")
+    @ApiModelProperty(value = "购物卡金额", required = true)
+    private BigDecimal amount;
+
+    /**
+     * 商户提成比例
+     */
+    @NotNull(message = "商户提成比例不能为空")
+    @DecimalMin(value = "0", message = "商户提成比例不能小于0")
+    @DecimalMax(value = "100", message = "商户提成比例不能大于100")
+    @ApiModelProperty(value = "商户提成比例", required = true)
+    private BigDecimal commissionRate;
+
+    /**
+     * 库存
+     */
+    @NotNull(message = "库存不能为空")
+    @Min(value = 0, message = "库存不能小于0")
+    @ApiModelProperty(value = "库存", required = true)
+    private Integer stock;
+
+    /**
+     * 有效期开始日期
+     */
+    @NotNull(message = "有效期开始日期不能为空")
+    @ApiModelProperty(value = "有效期开始日期", required = true)
+    private LocalDate validStartDate;
+
+    /**
+     * 有效期结束日期
+     */
+    @NotNull(message = "有效期结束日期不能为空")
+    @ApiModelProperty(value = "有效期结束日期", required = true)
+    private LocalDate validEndDate;
+
+    /**
+     * 是否上架:1=上架,0=下架,不传默认下架
+     */
+    @ApiModelProperty("是否上架:1=上架,0=下架,不传默认下架")
+    private Integer isPublished;
+
+    /**
+     * 详情
+     */
+    @ApiModelProperty("详情")
+    private String description;
+}

+ 19 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageUpdateDTO.java

@@ -0,0 +1,19 @@
+package com.ylx.giftCard.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("购物卡管理编辑DTO")
+public class GiftCardManageUpdateDTO extends GiftCardManageSaveDTO {
+    private static final long serialVersionUID = -5113137396795744112L;
+
+    @NotNull(message = "购物卡ID不能为空")
+    @ApiModelProperty(value = "购物卡ID", required = true)
+    private Long id;
+}

+ 25 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardPublishStatusDTO.java

@@ -0,0 +1,25 @@
+package com.ylx.giftCard.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@Data
+@ApiModel("购物卡上架状态DTO")
+public class GiftCardPublishStatusDTO implements Serializable {
+    private static final long serialVersionUID = 910932605659176876L;
+
+    /**
+     * 购物卡ID
+     */
+    @NotNull(message = "购物卡ID不能为空")
+    @ApiModelProperty(value = "购物卡ID", required = true)
+    private Long id;
+
+    @NotNull(message = "上架状态不能为空")
+    @ApiModelProperty(value = "是否上架:1=上架,0=下架", required = true)
+    private Integer isPublished;
+}

+ 25 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardPurchaseDTO.java

@@ -0,0 +1,25 @@
+package com.ylx.giftCard.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import javax.validation.constraints.*;
+
+@ApiModel("购物卡购买DTO")
+@Data
+public class GiftCardPurchaseDTO {
+
+    @NotNull(message = "购物卡ID不能为空")
+    @Positive(message = "购物卡ID必须为正整数")
+    @ApiModelProperty("购物卡ID")
+    private Long id;
+
+    @NotNull(message = "购买数量不能为空")
+    @Min(value = 1, message = "购买数量必须大于0")
+    @ApiModelProperty("购买数量")
+    private Integer quantity;
+
+    @ApiModelProperty("商户id")
+    private String merchantId;
+
+}

+ 43 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardDetailVO.java

@@ -0,0 +1,43 @@
+package com.ylx.giftCard.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@ApiModel("购物卡详情DTO")
+@Data
+public class GiftCardDetailVO {
+
+    @ApiModelProperty("购物卡ID")
+    private Long id;
+
+    @ApiModelProperty("购物卡名称")
+    private String name;
+
+    @ApiModelProperty("价格(单位:元,保留两位小数)")
+    private BigDecimal price; // 建议用 BigDecimal 避免浮点误差
+
+    @ApiModelProperty("适用范围描述")
+    private String scopeDesc = "全平台可用";
+
+    @ApiModelProperty("有效期开始日期")
+    private LocalDate validStartDate;
+
+    @ApiModelProperty("有效期结束日期")
+    private LocalDate validEndDate;
+
+    @ApiModelProperty("服务商户描述")
+    private String merchantDesc = "全平台商户可用";
+
+    @ApiModelProperty("库存数量")
+    private Integer stock;
+
+    @ApiModelProperty("销量")
+    private Integer sales;
+
+    @ApiModelProperty("封面图URL")
+    private String coverUrl;
+}

+ 16 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManageDetailVO.java

@@ -0,0 +1,16 @@
+package com.ylx.giftCard.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("购物卡管理详情VO")
+public class GiftCardManageDetailVO extends GiftCardManagePageVO {
+    private static final long serialVersionUID = -5068920224630043312L;
+
+    @ApiModelProperty("详情")
+    private String description;
+}

+ 42 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManageExportVO.java

@@ -0,0 +1,42 @@
+package com.ylx.giftCard.domain.vo;
+
+import com.ylx.common.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@ApiModel("购物卡管理导出VO")
+public class GiftCardManageExportVO implements Serializable {
+    private static final long serialVersionUID = -1629841029985374035L;
+
+    @Excel(name = "购物卡ID", sort = 1)
+    private Long id;
+
+    @Excel(name = "购物卡名称", sort = 2)
+    private String name;
+
+    @Excel(name = "金额", sort = 3)
+    private BigDecimal amount;
+
+    @Excel(name = "缩略图", sort = 4)
+    private String imageUrl;
+
+    @Excel(name = "库存", sort = 5)
+    private Integer stock;
+
+    @Excel(name = "销量", sort = 6)
+    private Integer sales;
+
+    @Excel(name = "是否上架", sort = 7, readConverterExp = "0=下架,1=上架")
+    private Integer isPublished;
+
+    @Excel(name = "发布时间", sort = 8, width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    @Excel(name = "商户提成比例", sort = 9, suffix = "%")
+    private BigDecimal commissionRate;
+}

+ 54 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManagePageVO.java

@@ -0,0 +1,54 @@
+package com.ylx.giftCard.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.Date;
+
+@Data
+@ApiModel("购物卡管理分页VO")
+public class GiftCardManagePageVO implements Serializable {
+    private static final long serialVersionUID = -6990232175036599711L;
+
+    @ApiModelProperty("购物卡ID")
+    private Long id;
+
+    @ApiModelProperty("商户ID")
+    private String merchantId;
+
+    @ApiModelProperty("购物卡名称")
+    private String name;
+
+    @ApiModelProperty("购物卡金额")
+    private BigDecimal amount;
+
+    @ApiModelProperty("缩略图URL")
+    private String imageUrl;
+
+    @ApiModelProperty("商户提成比例")
+    private BigDecimal commissionRate;
+
+    @ApiModelProperty("库存")
+    private Integer stock;
+
+    @ApiModelProperty("销量")
+    private Integer sales;
+
+    @ApiModelProperty("是否上架:1=上架,0=下架")
+    private Integer isPublished;
+
+    @ApiModelProperty("发布时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    @ApiModelProperty("有效期开始日期")
+    private LocalDate validStartDate;
+
+    @ApiModelProperty("有效期结束日期")
+    private LocalDate validEndDate;
+}

+ 53 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardVO.java

@@ -0,0 +1,53 @@
+package com.ylx.giftCard.domain.vo;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.ylx.giftCard.domain.GiftCard;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 礼品卡视图对象(Value Object)类
+ * 用于封装礼品卡相关的数据,提供前端展示使用
+ */
+@Data
+public class GiftCardVO {
+
+    @ApiModelProperty("礼品卡ID")
+    private Long id;
+
+    @ApiModelProperty("礼品卡名称")
+    private String name;
+
+    @ApiModelProperty("礼品卡金额")
+    private BigDecimal amount;
+
+    @ApiModelProperty("礼品卡图片URL")
+    private String imageUrl;
+
+    @ApiModelProperty("销售数量")
+    private Integer sales;
+
+    @ApiModelProperty("库存数量")
+    private Integer stock;
+
+    @ApiModelProperty("售罄状态")
+    private Boolean isSoldOut;
+
+    /**
+     * 构造函数,用于将实体对象转换为视图对象
+     *
+     * @param entity 礼品卡实体对象
+     */
+    public GiftCardVO(GiftCard entity) {
+        this.id = entity.getId();
+        this.name = entity.getName();
+        this.amount = entity.getAmount();
+        this.imageUrl = entity.getImageUrl();
+        this.sales = entity.getSales();
+        this.stock = entity.getStock();
+        // 判断库存是否为null或小于等于0,若是则标记为售罄
+        this.isSoldOut = ObjectUtil.isNull(entity.getStock()) || entity.getStock() <= 0;
+    }
+}

+ 21 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/enums/GiftCardOrderStatusEnum.java

@@ -0,0 +1,21 @@
+package com.ylx.giftCard.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum GiftCardOrderStatusEnum {
+
+    WAIT_PAY(0, "待支付"),
+    PAID(1, "已支付"),
+    REFUNDED(2, "已退款"),
+    EXPIRED(3, "已过期"),
+    CANCEL(4, "已取消");
+
+    private final Integer code;
+    private final String info;
+
+    GiftCardOrderStatusEnum(Integer code, String info) {
+        this.code = code;
+        this.info = info;
+    }
+}

+ 16 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/mapper/GiftCardMapper.java

@@ -0,0 +1,16 @@
+package com.ylx.giftCard.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.giftCard.domain.GiftCard;
+import com.ylx.giftCard.domain.dto.GiftCardManageQueryDTO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Date;
+import java.util.List;
+
+public interface GiftCardMapper extends BaseMapper<GiftCard> {
+
+    List<GiftCard> selectManageList(@Param("dto") GiftCardManageQueryDTO dto,
+                                    @Param("beginCreateTime") Date beginCreateTime,
+                                    @Param("endCreateTime") Date endCreateTime);
+}

+ 8 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/mapper/GiftCardOrderMapper.java

@@ -0,0 +1,8 @@
+package com.ylx.giftCard.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.giftCard.domain.GiftCardOrder;
+
+public interface GiftCardOrderMapper extends BaseMapper<GiftCardOrder> {
+
+}

+ 17 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/service/IGiftCardOrderService.java

@@ -0,0 +1,17 @@
+package com.ylx.giftCard.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
+import com.ylx.common.core.domain.model.WxLoginUser;
+import com.ylx.giftCard.domain.GiftCard;
+import com.ylx.giftCard.domain.GiftCardOrder;
+import com.ylx.massage.domain.TWxUser;
+
+public interface IGiftCardOrderService extends IService<GiftCardOrder> {
+
+    GiftCardOrder buildOrder(GiftCard card, Integer quantity, String merchantId, WxLoginUser wxLoginUser);
+
+    void cancelOrder(Long id);
+
+    void processGiftCardPayment(WxPayOrderNotifyV3Result.DecryptNotifyResult result, TWxUser wxUser, GiftCardOrder cardOrder);
+}

+ 40 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/service/IGiftCardService.java

@@ -0,0 +1,40 @@
+package com.ylx.giftCard.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.giftCard.domain.GiftCard;
+import com.ylx.giftCard.domain.dto.GiftCardManageQueryDTO;
+import com.ylx.giftCard.domain.dto.GiftCardManageSaveDTO;
+import com.ylx.giftCard.domain.dto.GiftCardManageUpdateDTO;
+import com.ylx.giftCard.domain.dto.GiftCardPublishStatusDTO;
+import com.ylx.giftCard.domain.dto.GiftCardPurchaseDTO;
+import com.ylx.giftCard.domain.vo.GiftCardDetailVO;
+import com.ylx.giftCard.domain.vo.GiftCardManageDetailVO;
+import com.ylx.giftCard.domain.vo.GiftCardManageExportVO;
+import com.ylx.giftCard.domain.vo.GiftCardManagePageVO;
+import com.ylx.giftCard.domain.vo.GiftCardVO;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IGiftCardService {
+
+    Page<GiftCardVO> getGiftCardPage(Page<GiftCard> page);
+
+    Map<String, Object> purchaseGiftCard(GiftCardPurchaseDTO dto);
+
+    GiftCardDetailVO getGiftCardDetail(Long id);
+
+    Page<GiftCardManagePageVO> getManagePage(Page<GiftCard> page, GiftCardManageQueryDTO dto);
+
+    List<GiftCardManageExportVO> getManageExportList(GiftCardManageQueryDTO dto);
+
+    GiftCardManageDetailVO getManageDetail(Long id);
+
+    void addGiftCard(GiftCardManageSaveDTO dto);
+
+    void updateGiftCard(GiftCardManageUpdateDTO dto);
+
+    void updatePublishStatus(GiftCardPublishStatusDTO dto);
+
+    void deleteGiftCard(Long id);
+}

+ 208 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardOrderServiceImpl.java

@@ -0,0 +1,208 @@
+package com.ylx.giftCard.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
+import com.ylx.common.core.domain.model.WxLoginUser;
+import com.ylx.common.exception.ServiceException;
+import com.ylx.common.utils.DateUtils;
+import com.ylx.giftCard.domain.GiftCard;
+import com.ylx.giftCard.domain.GiftCardOrder;
+import com.ylx.giftCard.enums.GiftCardOrderStatusEnum;
+import com.ylx.giftCard.mapper.GiftCardOrderMapper;
+import com.ylx.giftCard.service.IGiftCardOrderService;
+import com.ylx.massage.domain.TJs;
+import com.ylx.massage.domain.TWxUser;
+import com.ylx.massage.service.TJsService;
+import com.ylx.massage.service.TWxUserService;
+import com.ylx.shopingfundsdetail.domain.vo.ShoppingFundsDetailAddDto;
+import com.ylx.shopingfundsdetail.service.ShoppingFundsDetailService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+@Slf4j // 添加日志注解
+@Service
+public class GiftCardOrderServiceImpl extends ServiceImpl<GiftCardOrderMapper, GiftCardOrder> implements IGiftCardOrderService {
+
+    @Resource
+    private TJsService jsService;
+    @Resource
+    private TWxUserService wxUserService;
+    @Resource
+    private ShoppingFundsDetailService shoppingFundsDetailService;
+    // 充值
+    public static final int EXPENSE_TYPE_RECHARGE = 0;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public GiftCardOrder buildOrder(GiftCard card, Integer quantity, String merchantId, WxLoginUser wxLoginUser) {
+
+        // 1. 参数校验
+        if (ObjectUtil.isNull(card)) {
+            throw new ServiceException("购物卡信息不能为空");
+        }
+        if (ObjectUtil.isNull(quantity) || quantity <= 0) {
+            throw new ServiceException("购买数量必须大于0");
+        }
+
+        // 2. 创建订单对象
+        GiftCardOrder order = new GiftCardOrder();
+
+        // 4. 生成唯一订单号(使用更安全的方式)
+        String orderNo = generateUniqueOrderNo();
+        order.setOrderNo(orderNo);
+
+        // 5. 设置购物卡信息
+        setGiftCardInfo(order, card);
+
+        // 6. 设置用户信息
+        setUserInfo(order, wxLoginUser);
+
+        // 7. 设置商户信息
+        setMerchantInfo(order, merchantId);
+
+        // 8. 计算金额
+        calculateAmount(order, card, quantity);
+        order.setPurchaseQuantity(quantity);
+
+        // 9. 设置订单状态和时间
+        order.setStatus(GiftCardOrderStatusEnum.WAIT_PAY.getCode()); // 待支付
+        order.setCreateTime(DateUtils.getNowDate());
+        order.setUpdateTime(order.getCreateTime());
+        int rowsAffected = this.baseMapper.insert(order);
+        if (rowsAffected <= 0) {
+            log.warn("购物卡订单创建失败,购物卡ID: {},下单人ID: {}", card.getId(), wxLoginUser.getId());
+            return null;
+        }
+        return order;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void cancelOrder(Long id) {
+        // 1. 查询订单
+        GiftCardOrder order = this.getById(id);
+        if (ObjectUtil.isNull(order)) {
+            throw new ServiceException("订单不存在");
+        }
+        // 仅待支付订单可取消
+        if (!GiftCardOrderStatusEnum.PAID.getCode().equals(order.getStatus())) {
+            throw new ServiceException("当前订单状态不支持取消");
+        }
+        // 2. 修改订单状态为已取消
+        order.setStatus(GiftCardOrderStatusEnum.CANCEL.getCode());
+        order.setUpdateTime(DateUtils.getNowDate());
+        boolean update = this.updateById(order);
+        if (!update) {
+            log.error("订单取消失败,订单号:{}", order.getOrderNo());
+            throw new ServiceException("订单取消失败");
+        }
+        log.info("订单取消成功,订单号:{},购物卡ID:{}", order.getOrderNo(), order.getGiftCardId());
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void processGiftCardPayment(WxPayOrderNotifyV3Result.DecryptNotifyResult result, TWxUser wxUser,GiftCardOrder cardOrder) {
+
+        // 更新订单状态
+        this.lambdaUpdate()
+                .set(GiftCardOrder::getStatus, GiftCardOrderStatusEnum.PAID.getCode())
+                .eq(GiftCardOrder::getId, cardOrder.getId())
+                .update();
+
+        // 计算充值金额
+        Integer totalCent = result.getAmount().getTotal();
+        BigDecimal payAmount = new BigDecimal(totalCent).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
+
+        // 更新用户余额
+        BigDecimal oldBalance = wxUser.getdBalance();
+        BigDecimal newBalance = oldBalance.add(payAmount);
+        this.wxUserService.lambdaUpdate()
+                .set(TWxUser::getdBalance, newBalance)
+                .eq(TWxUser::getId, wxUser.getId())
+                .update();
+
+        // 记录购物金明细
+        ShoppingFundsDetailAddDto dto = new ShoppingFundsDetailAddDto();
+        dto.setUserId(wxUser.getId());
+        dto.setAmount(payAmount);
+        dto.setOrderNo(result.getOutTradeNo());
+        dto.setExpenseType(EXPENSE_TYPE_RECHARGE);
+        dto.setBalance(newBalance);
+        dto.setGiftCardId(cardOrder.getGiftCardId());
+        shoppingFundsDetailService.addShoppingFundsDetail(dto);
+    }
+
+    /**
+     * 生成唯一订单号
+     */
+    private String generateUniqueOrderNo() {
+        // 使用时间戳 + 随机数 + 更多信息避免冲突
+        long timestamp = System.currentTimeMillis();
+        String randomNum = RandomUtil.randomNumbers(6);
+        // 可以加入用户ID后几位、线程ID等进一步降低冲突概率
+        String suffix = String.valueOf(timestamp % 1000000).substring(0, 3); // 取时间戳后3位
+        return "GC" + timestamp + randomNum + suffix;
+    }
+
+    /**
+     * 设置购物卡信息
+     */
+    private void setGiftCardInfo(GiftCardOrder order, GiftCard card) {
+        order.setGiftCardId(card.getId());
+        order.setGiftCardName(card.getName());
+        order.setGiftCardAmount(card.getAmount());
+        order.setCommissionRate(card.getCommissionRate());
+    }
+
+    /**
+     * 设置用户信息
+     */
+    private void setUserInfo(GiftCardOrder order, WxLoginUser wxLoginUser) {
+        order.setUserId(wxLoginUser.getId());
+        order.setUserName(wxLoginUser.getUsername());
+        order.setUserPhone(wxLoginUser.getCPhone());
+    }
+
+    /**
+     * 设置商户信息
+     */
+    private void setMerchantInfo(GiftCardOrder order, String merchantId) {
+        if (StrUtil.isEmpty(merchantId)) {
+            log.warn("商户ID为空,跳过商户信息查询");
+            return;
+        }
+
+        TJs merchant = this.jsService.getById(merchantId);
+        if (ObjectUtil.isNotNull(merchant)) {
+            order.setMerchantId(merchantId);
+            order.setMerchantName(merchant.getcName());
+            order.setMerchantNickName(merchant.getcNickName());
+            // 注意:如果 TJs 表有收款账号字段,可以在这里设置
+            // order.setMerchantAccount(merchant.getAccount());
+        }
+    }
+
+    /**
+     * 计算金额
+     */
+    private void calculateAmount(GiftCardOrder order, GiftCard card, Integer quantity) {
+        BigDecimal payAmount = card.getAmount()
+                .multiply(new BigDecimal(quantity))
+                .setScale(2, RoundingMode.HALF_UP); // 保留两位小数
+
+        order.setPayAmount(payAmount);
+
+        BigDecimal commissionAmount = payAmount
+                .multiply(card.getCommissionRate())
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+
+        order.setCommissionAmount(commissionAmount);
+    }
+}

+ 453 - 0
nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardServiceImpl.java

@@ -0,0 +1,453 @@
+package com.ylx.giftCard.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+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.plugins.pagination.Page;
+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.DateUtils;
+import com.ylx.common.utils.SecurityUtils;
+import com.ylx.common.weixinPay.enums.WxPayTypeEnum;
+import com.ylx.common.weixinPay.service.WxPayV3Service;
+import com.ylx.giftCard.domain.GiftCard;
+import com.ylx.giftCard.domain.GiftCardOrder;
+import com.ylx.giftCard.domain.dto.GiftCardManageQueryDTO;
+import com.ylx.giftCard.domain.dto.GiftCardManageSaveDTO;
+import com.ylx.giftCard.domain.dto.GiftCardManageUpdateDTO;
+import com.ylx.giftCard.domain.dto.GiftCardPublishStatusDTO;
+import com.ylx.giftCard.domain.dto.GiftCardPurchaseDTO;
+import com.ylx.giftCard.domain.vo.GiftCardDetailVO;
+import com.ylx.giftCard.domain.vo.GiftCardManageDetailVO;
+import com.ylx.giftCard.domain.vo.GiftCardManageExportVO;
+import com.ylx.giftCard.domain.vo.GiftCardManagePageVO;
+import com.ylx.giftCard.domain.vo.GiftCardVO;
+import com.ylx.giftCard.mapper.GiftCardMapper;
+import com.ylx.giftCard.service.IGiftCardOrderService;
+import com.ylx.giftCard.service.IGiftCardService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> implements IGiftCardService {
+
+    @Resource
+    private IGiftCardOrderService giftCardOrderService;
+    @Resource
+    private WxPayV3Service wxPayV3Service;
+
+    private static final int NOT_DELETE = 0;
+    private static final int PUBLISHED = 1;
+    private static final int UNPUBLISHED = 0;
+    private static final int DELETE = 1;
+
+    @Override
+    public Page<GiftCardVO> getGiftCardPage(Page<GiftCard> page) {
+
+        LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(GiftCard::getIsDelete, NOT_DELETE);
+        wrapper.eq(GiftCard::getIsPublished, PUBLISHED);
+        wrapper.orderByDesc(GiftCard::getCreateTime);
+
+        Page<GiftCard> giftCardPage = this.baseMapper.selectPage(page, wrapper);
+        Page<GiftCardVO> pageData = new Page<>(
+                giftCardPage.getCurrent(),
+                giftCardPage.getSize(),
+                giftCardPage.getTotal()
+        );
+
+        if (CollectionUtil.isNotEmpty(giftCardPage.getRecords())) {
+            List<GiftCardVO> voList = giftCardPage.getRecords().stream()
+                    .map(GiftCardVO::new)
+                    .collect(Collectors.toList());
+            pageData.setRecords(voList);
+        }
+
+        return pageData;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> purchaseGiftCard(GiftCardPurchaseDTO dto) {
+
+        // 1. 获取当前用户
+        WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser();
+        if (ObjectUtil.isNull(wxLoginUser)) {
+            log.warn("用户未登录,无法创建订单");
+            throw new ServiceException("用户未登录");
+        }
+
+        Long id = dto.getId();
+        Integer quantity = dto.getQuantity();
+        String merchantId = dto.getMerchantId();
+
+        // 2. 查询并校验购物卡
+        GiftCard card = this.getById(id);
+        validateGiftCard(card, quantity);
+
+        // 3. 乐观锁扣减库存(增加状态校验,防止无效更新)
+        int rowsAffected = deductStockOptimisticLock(card.getId(), dto.getQuantity());
+        if (rowsAffected <= 0) {
+            log.warn("购买失败,库存不足或商品不存在,购物卡ID: {}", id);
+            throw new ServiceException("库存不足或商品状态异常");
+        }
+
+        log.info("购买成功,购物卡ID: {}, 数量: {}", id, quantity);
+
+        // 4. 创建订单
+        GiftCardOrder order = this.giftCardOrderService.buildOrder(card, quantity, merchantId, wxLoginUser);
+        if(ObjectUtil.isNull(order)){
+            log.warn("购物卡订单创建失败,购物卡ID: {},下单人ID: {}", card.getId(), wxLoginUser.getId());
+            throw new ServiceException("购物卡订单创建失败");
+        }
+
+        log.info("购物卡订单创建,购物卡ID: {}, 订单编号: {}", id, order.getOrderNo());
+
+        //  5. 调用微信支付
+        return createWxPayOrder(order, wxLoginUser);
+    }
+
+    @Override
+    public GiftCardDetailVO getGiftCardDetail(Long id) {
+
+        LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(GiftCard::getId, id)
+                .eq(GiftCard::getIsDelete, NOT_DELETE)
+                .eq(GiftCard::getIsPublished, PUBLISHED);
+
+        GiftCard card = this.getOne(wrapper);
+        if (ObjectUtil.isNull(card)) {
+            return null;
+        }
+
+        GiftCardDetailVO vo = new GiftCardDetailVO();
+        BeanUtil.copyProperties(card, vo);
+        return vo;
+    }
+
+    @Override
+    public Page<GiftCardManagePageVO> getManagePage(Page<GiftCard> page, GiftCardManageQueryDTO dto) {
+        Page<GiftCard> pageParam = ObjectUtil.isNull(page) ? new Page<>(1, 10) : page;
+        LambdaQueryWrapper<GiftCard> wrapper = buildManageQueryWrapper(dto);
+
+        Page<GiftCard> entityPage = this.baseMapper.selectPage(pageParam, wrapper);
+        Page<GiftCardManagePageVO> pageData = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
+        pageData.setPages(entityPage.getPages());
+        if (CollectionUtil.isNotEmpty(entityPage.getRecords())) {
+            pageData.setRecords(entityPage.getRecords().stream()
+                    .map(this::toManagePageVO)
+                    .collect(Collectors.toList()));
+        }
+        return pageData;
+    }
+
+    @Override
+    public List<GiftCardManageExportVO> getManageExportList(GiftCardManageQueryDTO dto) {
+        GiftCardManageQueryDTO query = ObjectUtil.isNull(dto) ? new GiftCardManageQueryDTO() : dto;
+        checkCreateDateRange(query.getBeginCreateDate(), query.getEndCreateDate());
+        if (StrUtil.isNotBlank(query.getName())) {
+            query.setName(StrUtil.trim(query.getName()));
+        }
+        List<GiftCard> entityList = this.baseMapper.selectManageList(
+                query,
+                toStartDate(query.getBeginCreateDate()),
+                toEndDate(query.getEndCreateDate())
+        );
+        return entityList.stream().map(this::toManageExportVO).collect(Collectors.toList());
+    }
+
+    @Override
+    public GiftCardManageDetailVO getManageDetail(Long id) {
+        GiftCard card = getActiveGiftCard(id);
+        GiftCardManageDetailVO vo = new GiftCardManageDetailVO();
+        BeanUtil.copyProperties(card, vo);
+        return vo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void addGiftCard(GiftCardManageSaveDTO dto) {
+        checkSaveParam(dto);
+
+        GiftCard entity = new GiftCard();
+        fillGiftCard(entity, dto, normalizePublishStatus(dto.getIsPublished(), UNPUBLISHED));
+        entity.setId(null);
+        entity.setSales(0);
+        entity.setCreateBy(SecurityUtils.getUsername());
+        entity.setCreateTime(DateUtils.getNowDate());
+        entity.setUpdateBy(SecurityUtils.getUsername());
+        entity.setUpdateTime(DateUtils.getNowDate());
+
+        int insertResult = this.baseMapper.insert(entity);
+        if (insertResult <= 0) {
+            throw new ServiceException("新增购物卡失败");
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateGiftCard(GiftCardManageUpdateDTO dto) {
+        if (ObjectUtil.isNull(dto) || ObjectUtil.isNull(dto.getId())) {
+            throw new ServiceException("购物卡ID不能为空");
+        }
+        checkSaveParam(dto);
+
+        GiftCard entity = getActiveGiftCard(dto.getId());
+        Integer publishStatus = normalizePublishStatus(dto.getIsPublished(), entity.getIsPublished());
+        fillGiftCard(entity, dto, publishStatus);
+        entity.setUpdateBy(SecurityUtils.getUsername());
+        entity.setUpdateTime(DateUtils.getNowDate());
+
+        int updateResult = this.baseMapper.updateById(entity);
+        if (updateResult <= 0) {
+            throw new ServiceException("编辑购物卡失败");
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updatePublishStatus(GiftCardPublishStatusDTO dto) {
+        if (ObjectUtil.isNull(dto) || ObjectUtil.isNull(dto.getId())) {
+            throw new ServiceException("购物卡ID不能为空");
+        }
+        Integer publishStatus = normalizePublishStatus(dto.getIsPublished(), null);
+        GiftCard entity = getActiveGiftCard(dto.getId());
+        entity.setIsPublished(publishStatus);
+        entity.setUpdateBy(SecurityUtils.getUsername());
+        entity.setUpdateTime(DateUtils.getNowDate());
+
+        int updateResult = this.baseMapper.updateById(entity);
+        if (updateResult <= 0) {
+            throw new ServiceException("修改购物卡上架状态失败");
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteGiftCard(Long id) {
+        int deleteResult = this.baseMapper.deleteById(id);
+        if (deleteResult <= 0) {
+            throw new ServiceException("删除购物卡失败");
+        }
+    }
+
+    /**
+     * 校验购物卡有效性
+     */
+    private void validateGiftCard(GiftCard card, Integer quantity) {
+        if (ObjectUtil.isNull(card)) {
+            throw new ServiceException("商品不存在");
+        }
+        if (card.getIsDelete() != NOT_DELETE) {
+            log.warn("购买失败,购物卡已删除,ID: {}", card.getId());
+            throw new ServiceException("购物卡已删除");
+        }
+        if (card.getIsPublished() != PUBLISHED) {
+            log.warn("购买失败,购物卡未上架,ID: {}", card.getId());
+            throw new ServiceException("购物卡未上架");
+        }
+        if (card.getStock() < quantity) {
+            log.warn("购买失败,库存不足,ID: {},库存: {},需求数量: {}", card.getId(), card.getStock(), quantity);
+            throw new ServiceException("库存不足");
+        }
+
+    }
+
+    /**
+     * 乐观锁扣减库存(增加状态条件,确保只更新有效记录)
+     */
+    private int deductStockOptimisticLock(Long cardId, Integer quantity) {
+        LambdaUpdateWrapper<GiftCard> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.eq(GiftCard::getId, cardId)
+                .eq(GiftCard::getIsDelete, NOT_DELETE)
+                .eq(GiftCard::getIsPublished, PUBLISHED)
+                .ge(GiftCard::getStock, quantity) // 库存充足时才更新
+                .setSql("stock = stock - " + quantity + ", sales = sales + " + quantity);
+        return baseMapper.update(null, wrapper);
+    }
+
+    /**
+     * 补偿回滚库存
+     */
+    private void recoverStock(Long cardId, Integer num) {
+        GiftCard updateEntity = new GiftCard();
+        LambdaUpdateWrapper<GiftCard> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(GiftCard::getId, cardId)
+                .eq(GiftCard::getIsDelete, NOT_DELETE);
+        updateWrapper.setSql("stock = stock + #{num}, sales = sales - #{num}");
+        // 数据放到实体里
+        updateEntity.setStock(num);
+        baseMapper.update(updateEntity, updateWrapper);
+    }
+
+    /**
+     * 创建微信支付订单(事务外执行,减少事务时长)
+     */
+    private Map<String, Object> createWxPayOrder(GiftCardOrder order, WxLoginUser wxLoginUser) {
+        try {
+            return wxPayV3Service.createV3JsapiOrder(
+                    order.getOrderNo(),
+                    order.getPayAmount(),
+                    "购物卡购买",
+                    wxLoginUser.getCOpenid(),
+                    WxPayTypeEnum.GIFT_CARD.getCode()
+            );
+        } catch (Exception e) {
+            log.error("微信支付下单失败,订单号: {}", order.getOrderNo(), e);
+            // 支付失败:恢复库存、修改订单为取消
+            recoverStock(order.getGiftCardId(), order.getPurchaseQuantity());
+            this.giftCardOrderService.cancelOrder(order.getId());
+            throw new ServiceException("支付服务异常,请稍后重试");
+        }
+    }
+
+    private GiftCard getActiveGiftCard(Long id) {
+        if (ObjectUtil.isNull(id)) {
+            throw new ServiceException("购物卡ID不能为空");
+        }
+        if (id <= 0) {
+            throw new ServiceException("购物卡ID不正确");
+        }
+
+        LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(GiftCard::getId, id).eq(GiftCard::getIsDelete, NOT_DELETE);
+        GiftCard entity = this.baseMapper.selectOne(wrapper);
+        if (ObjectUtil.isNull(entity)) {
+            throw new ServiceException("购物卡不存在或已删除");
+        }
+        return entity;
+    }
+
+    private void checkSaveParam(GiftCardManageSaveDTO dto) {
+        if (ObjectUtil.isNull(dto)) {
+            throw new ServiceException("购物卡参数不能为空");
+        }
+        if (StrUtil.isBlank(dto.getName())) {
+            throw new ServiceException("购物卡名称不能为空");
+        }
+        if (StrUtil.length(StrUtil.trim(dto.getName())) > 20) {
+            throw new ServiceException("购物卡名称不能超过20个字符");
+        }
+        if (StrUtil.isBlank(dto.getImageUrl())) {
+            throw new ServiceException("图片不能为空");
+        }
+        if (ObjectUtil.isNull(dto.getAmount()) || dto.getAmount().signum() <= 0) {
+            throw new ServiceException("购物卡金额必须大于0");
+        }
+        if (ObjectUtil.isNull(dto.getCommissionRate())
+                || dto.getCommissionRate().signum() < 0
+                || dto.getCommissionRate().compareTo(new BigDecimal("100")) > 0) {
+            throw new ServiceException("商户提成比例必须在0到100之间");
+        }
+        if (ObjectUtil.isNull(dto.getStock()) || dto.getStock() < 0) {
+            throw new ServiceException("库存不能小于0");
+        }
+        if (ObjectUtil.isNull(dto.getValidStartDate()) || ObjectUtil.isNull(dto.getValidEndDate())) {
+            throw new ServiceException("有效期不能为空");
+        }
+        if (dto.getValidStartDate().isAfter(dto.getValidEndDate())) {
+            throw new ServiceException("有效期开始日期不能晚于结束日期");
+        }
+    }
+
+    private void checkCreateDateRange(LocalDate beginDate, LocalDate endDate) {
+        if (ObjectUtil.isNotNull(beginDate) && ObjectUtil.isNotNull(endDate) && beginDate.isAfter(endDate)) {
+            throw new ServiceException("创建开始日期不能晚于结束日期");
+        }
+    }
+
+    private LambdaQueryWrapper<GiftCard> buildManageQueryWrapper(GiftCardManageQueryDTO dto) {
+        GiftCardManageQueryDTO query = ObjectUtil.isNull(dto) ? new GiftCardManageQueryDTO() : dto;
+        checkCreateDateRange(query.getBeginCreateDate(), query.getEndCreateDate());
+
+        LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(GiftCard::getIsDelete, NOT_DELETE)
+                .like(StrUtil.isNotBlank(query.getName()), GiftCard::getName, StrUtil.trim(query.getName()))
+                .eq(ObjectUtil.isNotNull(query.getIsPublished()), GiftCard::getIsPublished, query.getIsPublished())
+                .ge(ObjectUtil.isNotNull(query.getBeginCreateDate()), GiftCard::getCreateTime, toStartDate(query.getBeginCreateDate()))
+                .le(ObjectUtil.isNotNull(query.getEndCreateDate()), GiftCard::getCreateTime, toEndDate(query.getEndCreateDate()))
+                .orderByDesc(GiftCard::getCreateTime)
+                .orderByDesc(GiftCard::getId);
+        return wrapper;
+    }
+
+    /**
+     * 填充购物卡实体
+     * @param entity
+     * @param dto
+     * @param publishStatus
+     */
+    private void fillGiftCard(GiftCard entity, GiftCardManageSaveDTO dto, Integer publishStatus) {
+        entity.setMerchantId(StrUtil.trim(dto.getMerchantId()));
+        entity.setName(StrUtil.trim(dto.getName()));
+        entity.setImageUrl(StrUtil.trim(dto.getImageUrl()));
+        entity.setAmount(dto.getAmount());
+        entity.setCommissionRate(dto.getCommissionRate());
+        entity.setStock(dto.getStock());
+        entity.setValidStartDate(dto.getValidStartDate());
+        entity.setValidEndDate(dto.getValidEndDate());
+        entity.setDescription(dto.getDescription());
+        entity.setIsPublished(publishStatus);
+    }
+
+    /**
+     * 规范化上架状态值
+     * @param value
+     * @param defaultValue
+     * @return Integer
+     */
+    private Integer normalizePublishStatus(Integer value, Integer defaultValue) {
+        if (ObjectUtil.isNull(value)) {
+            if (ObjectUtil.isNull(defaultValue)) {
+                throw new ServiceException("上架状态不能为空");
+            }
+            return defaultValue;
+        }
+        if (value == UNPUBLISHED || value == PUBLISHED) {
+            return value;
+        }
+        throw new ServiceException("上架状态值不正确");
+    }
+
+    private GiftCardManagePageVO toManagePageVO(GiftCard entity) {
+        GiftCardManagePageVO vo = new GiftCardManagePageVO();
+        BeanUtil.copyProperties(entity, vo);
+        return vo;
+    }
+
+    private GiftCardManageExportVO toManageExportVO(GiftCard entity) {
+        GiftCardManageExportVO vo = new GiftCardManageExportVO();
+        BeanUtil.copyProperties(entity, vo);
+        return vo;
+    }
+
+    private Date toStartDate(LocalDate date) {
+        if (ObjectUtil.isNull(date)) {
+            return null;
+        }
+        return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
+    }
+
+    private Date toEndDate(LocalDate date) {
+        if (ObjectUtil.isNull(date)) {
+            return null;
+        }
+        return Date.from(date.plusDays(1).atStartOfDay(ZoneId.systemDefault()).minusNanos(1).toInstant());
+    }
+
+}

+ 26 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/constant/CommonConstant.java

@@ -0,0 +1,26 @@
+package com.ylx.massage.constant;
+
+public interface CommonConstant {
+
+    /**
+     * {@code 500 Server Error} (HTTP/1.0 - RFC 1945)
+     */
+    Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
+
+    /**
+     * {@code 404 Not Found} (HTTP/1.0 - RFC 1945)
+     */
+    Integer SC_INTERNAL_NOT_FOUND_404 = 404;
+
+    /**
+     * {@code 200 OK} (HTTP/1.0 - RFC 1945)
+     */
+    Integer SC_OK_200 = 200;
+
+    /**
+     * 访问权限认证未通过 510
+     */
+    Integer SC_JEECG_NO_AUTHZ = 510;
+
+
+}

+ 87 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/controller/CityOperationApplicationController.java

@@ -0,0 +1,87 @@
+package com.ylx.massage.controller;
+
+
+
+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.extension.plugins.pagination.Page;
+import com.ylx.common.core.domain.R;
+import com.ylx.massage.domain.Area;
+import com.ylx.massage.domain.CityOperationApplication;
+import com.ylx.massage.service.CityOperationApplicationService;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 城市管理表(CityOperationApplication)表控制层
+ *
+ * @author makejava
+ * @since 2026-06-04 14:19:49
+ */
+@RestController
+@RequestMapping("cityOperationApplication")
+public class CityOperationApplicationController {
+    /**
+     * 服务对象
+     */
+    @Autowired
+    private CityOperationApplicationService cityOperationApplicationService;
+
+    /**
+     * 分页查询所有数据
+     *
+     * @param page 分页对象
+     * @param cityOperationApplication 查询实体
+     * @return 所有数据
+     */
+    @GetMapping("/list")
+    public R selectAll(Page<CityOperationApplication> page, CityOperationApplication cityOperationApplication) {
+        return R.ok(this.cityOperationApplicationService.page(page, new QueryWrapper<>(cityOperationApplication)));
+    }
+
+    /**
+     * 通过主键查询单条数据
+     *
+     * @param id 主键
+     * @return 单条数据
+     */
+    @GetMapping("{id}")
+    public R selectOne(@PathVariable Serializable id) {
+        return R.ok(this.cityOperationApplicationService.getById(id));
+    }
+
+    /**
+     * 新增数据
+     *
+     * @param cityOperationApplication 实体对象
+     * @return 新增结果
+     */
+    @PostMapping("/saveCity")
+    public R insert(@RequestBody CityOperationApplication cityOperationApplication) {
+        return R.ok(this.cityOperationApplicationService.save(cityOperationApplication));
+    }
+    /**
+     * 根据商户ID查询城市管理列表
+     * @param merchantId
+     * @return R
+     */
+    @GetMapping("/getCityList")
+    @ApiOperation("根据商户ID查询城市管理列表")
+    public R getCityList(@RequestParam String merchantId) {
+        LambdaQueryWrapper<CityOperationApplication> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(CityOperationApplication::getMerchantId, merchantId);
+        List<CityOperationApplication> list = this.cityOperationApplicationService.list(queryWrapper);
+        if (CollectionUtil.isEmpty(list)) {
+            return R.fail("未找到此数据");
+        }
+        return R.ok(list);
+    }
+
+
+
+}
+

+ 58 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/controller/MassageRecommendController.java

@@ -0,0 +1,58 @@
+package com.ylx.massage.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.core.domain.R;
+import com.ylx.massage.domain.dto.MassageAllMerchantsDto;
+import com.ylx.massage.domain.dto.MassageMerchantRecommendDto;
+import com.ylx.massage.domain.vo.MassageAllMerchantsVo;
+import com.ylx.massage.domain.vo.MassageProjectRecommendVo;
+import com.ylx.massage.domain.vo.MerchantVo;
+import com.ylx.massage.service.IMassageRecommendService;
+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.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 类描述:按摩推荐
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/2 16:36
+ */
+@Slf4j
+@RestController
+@Api(tags = {"首页按摩推荐"})
+@RequestMapping("/home/massage")
+public class MassageRecommendController {
+
+    @Autowired
+    private IMassageRecommendService massageRecommendService;
+
+    @ApiOperation(value="首页按摩商户推荐", notes="首页按摩商户推荐")
+    @PostMapping(value = "/getMerchantRecommend")
+    public R<List<MerchantVo>> getMerchantRecommend(@RequestBody MassageMerchantRecommendDto dto) {
+        List<MerchantVo> massageRecommendVos = massageRecommendService.getMerchantRecommend(dto);
+        return R.ok(massageRecommendVos);
+    }
+
+    @ApiOperation(value="首页按摩项目推荐", notes="首页按摩项目推荐")
+    @GetMapping(value = "/getMassageProjectRecommend")
+    public R<Page<MassageProjectRecommendVo>> getMassageProjectRecommend(@RequestParam("pageNo") Integer pageNo, @RequestParam("pageSize") Integer pageSize,@RequestParam("cityCode") String cityCode) {
+        Page<MassageProjectRecommendVo> page = new Page<>(pageNo, pageSize);
+        return R.ok( massageRecommendService.getMassageProjectRecommend(page,cityCode));
+    }
+
+    @ApiOperation(value="首页按摩推荐商户点击全部查询按摩所有商户", notes="首页按摩推荐商户点击全部查询按摩所有商户")
+    @PostMapping(value = "/getMassageAllMerchants")
+    public R<Page<MassageAllMerchantsVo>> getMassageAllMerchants(@Validated @RequestBody MassageAllMerchantsDto dto) {
+        Page<MassageAllMerchantsVo> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        return R.ok( massageRecommendService.getMassageAllMerchants(page,dto));
+    }
+
+
+}

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

@@ -27,6 +27,11 @@ public class AfterSaleOrder implements Serializable {
     /** 用户openId */
     private String openId;
 
+    /**
+     * 用户Id
+     */
+    private String userId;
+
     /**
      * 售后类型
      * 1退货 2换货 3:未收到货

+ 88 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/CityOperationApplication.java

@@ -0,0 +1,88 @@
+package com.ylx.massage.domain;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 城市管理表(CityOperationApplication)表实体类
+ *
+ * @author makejava
+ * @since 2026-06-04 14:27:32
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName(value = "city_operation_application",autoResultMap = true)
+public class CityOperationApplication  implements Serializable {
+//主键ID
+@TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+//城市编码 (例如: 110000)
+@TableField("city_code")
+    private String cityCode;
+//城市名称 (冗余字段,方便查询,例如: 北京市)
+@TableField("city_name")
+    private String cityName;
+//运营中心ID
+@TableField("operation_center_id")
+    private Long operationCenterId;
+//运营中心名称
+@TableField("operation_center_name")
+    private String operationCenterName;
+//实际审批通过时间
+@TableField("actual_approval_time")
+    private Date actualApprovalTime;
+//申请原因 (限制500字以内)
+    @TableField("apply_reason")
+    private String applyReason;
+
+//状态 (0:待审核, 1:已通过, 2:已驳回)
+    @TableField("status")
+    private Integer status;
+//商户ID
+    @TableField("merchant_id")
+    private String merchantId;
+    /**
+     * 创建时间
+     */
+    @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;
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_by")
+    private String createBy;
+
+    /**
+     * 更新时间
+     */
+    @TableField(value = "update_by")
+    private String updateBy;
+
+    /**
+     * 逻辑删除:1已删除 0未删除
+     */
+    @TableField("is_delete")
+    private Integer isDelete;
+
+
+
+}
+

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

@@ -0,0 +1,69 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 合同记录实体类。
+ */
+@Data
+@TableName("contract_records")
+@ApiModel(value = "MerchantContractRecord", description = "合同记录")
+public class ContractRecord {
+
+    /**
+     * 主键ID。
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty("主键ID")
+    private Long id;
+
+    /**
+     * 商户ID。
+     */
+    @TableField("merchant_id")
+    @ApiModelProperty("商户ID")
+    private Integer merchantId;
+
+    /**
+     * 合同名称。
+     */
+    @TableField("contract_name")
+    @ApiModelProperty("合同名称")
+    private String contractName;
+
+    /**
+     * 上传文件URL。
+     */
+    @TableField("file_url")
+    @ApiModelProperty("上传文件URL")
+    private String fileUrl;
+
+    /**
+     * 合同签定时间。
+     */
+    @TableField("sign_time")
+    @ApiModelProperty("合同签定时间")
+    private Date signTime;
+
+    /**
+     * 签订人姓名。
+     */
+    @TableField("signer_name")
+    @ApiModelProperty("签订人姓名")
+    private String signerName;
+
+    /**
+     * 创建时间。
+     */
+    @TableField("create_time")
+    @ApiModelProperty("创建时间")
+    private Date createTime;
+}

+ 89 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/ContractRecords.java

@@ -0,0 +1,89 @@
+package com.ylx.massage.domain;
+
+
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 合同记录表(ContractRecords)表实体类
+ *
+ * @author makejava
+ * @since 2026-06-04 16:45:28
+ */
+@SuppressWarnings("serial")
+public class ContractRecords {
+//主键ID
+    private Object id;
+//商户ID
+    private Integer merchantId;
+//合同名称
+    private String contractName;
+//上传文件URL
+    private String fileUrl;
+//合同签定时间
+    private Date signTime;
+//签订人姓名
+    private String signerName;
+//创建时间
+    private Date createTime;
+
+
+    public Object getId() {
+        return id;
+    }
+
+    public void setId(Object id) {
+        this.id = id;
+    }
+
+    public Integer getMerchantId() {
+        return merchantId;
+    }
+
+    public void setMerchantId(Integer merchantId) {
+        this.merchantId = merchantId;
+    }
+
+    public String getContractName() {
+        return contractName;
+    }
+
+    public void setContractName(String contractName) {
+        this.contractName = contractName;
+    }
+
+    public String getFileUrl() {
+        return fileUrl;
+    }
+
+    public void setFileUrl(String fileUrl) {
+        this.fileUrl = fileUrl;
+    }
+
+    public Date getSignTime() {
+        return signTime;
+    }
+
+    public void setSignTime(Date signTime) {
+        this.signTime = signTime;
+    }
+
+    public String getSignerName() {
+        return signerName;
+    }
+
+    public void setSignerName(String signerName) {
+        this.signerName = signerName;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+}
+

+ 104 - 26
nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaProject.java

@@ -4,9 +4,9 @@ import java.math.BigDecimal;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
 import com.ylx.common.annotation.Excel;
 import com.ylx.common.core.domain.BaseEntity;
+import lombok.Data;
 
 /**
  * 服务项目对象 ma_project
@@ -16,70 +16,148 @@ import com.ylx.common.core.domain.BaseEntity;
  */
 @ApiModel(value = "MaProject", description = "服务项目")
 @Data
-public class MaProject extends BaseEntity
-{
+public class MaProject extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
-    /** id */
+    /**
+     * id
+     */
     @ApiModelProperty("id")
     private Long id;
-
-    /** 项目名称 */
+    /**
+     * 项目ID
+     */
+    @Excel(name = "项目ID")
+    @ApiModelProperty("项目ID")
+    private String projectId;
+    /**
+     * 项目名称
+     */
     @Excel(name = "项目名称")
     @ApiModelProperty("项目名称")
     private String projectName;
 
-    /** 项目描述 */
+    /**
+     * 项目描述
+     */
     @Excel(name = "项目描述")
     @ApiModelProperty("项目描述")
     private String projectDescribe;
 
-    /** 项目时长(分) */
+    /**
+     * 项目时长(分)
+     */
     @Excel(name = "项目时长(分)")
     @ApiModelProperty("项目时长(分)")
-    private Long projectDuration;
+    private Integer projectDuration;
 
-    /** 项目选择人数 */
+    /**
+     * 项目选择人数
+     */
     @Excel(name = "项目选择人数")
     @ApiModelProperty("项目选择人数")
     private Long projectUsersNum;
 
-    /** 项目原价 */
+    /**
+     * 项目原价
+     */
     @Excel(name = "项目原价")
     @ApiModelProperty("项目原价")
     private BigDecimal projectOriginalPrice;
 
-    /** 项目现价 */
-    @Excel(name = "项目现价")
-    @ApiModelProperty("项目现价")
-    private BigDecimal projectCurrentPrice;
 
-    /** 项目主图 */
+    /**
+     * 项目价格上限
+     */
+    @Excel(name = "项目价格上限")
+    @ApiModelProperty("项目价格上限")
+    private BigDecimal projectMaxPrice;
+
+    /**
+     * 项目价格下限
+     */
+    @Excel(name = "项目价格下限")
+    @ApiModelProperty("项目价格下限")
+    private BigDecimal projectLowestPrice;
+
+    /**
+     * 我的售价
+     */
+    @Excel(name = "我的售价")
+    @ApiModelProperty("我的售价")
+    private BigDecimal projectCurrentPrice;
+    /**
+     * 商户手机号
+     */
+    @Excel(name = "商户手机号")
+    @ApiModelProperty("商户手机号")
+    private String merchantPhone;
+
+    /**
+     * 项目主图
+     */
     @Excel(name = "项目主图")
     @ApiModelProperty("项目主图")
     private String projectMasterImage;
-
-    /** 项目详情图 */
+    /**
+     * 商户类型:0-上门按摩,1-同城陪玩
+     */
+    @Excel(name = "商户类型:0-上门按摩,1-同城陪玩")
+    @ApiModelProperty("商户类型:0-上门按摩,1-同城陪玩")
+    private String merchantType;
+
+    /**
+     * 项目详情图
+     */
     @Excel(name = "项目详情图")
     @ApiModelProperty("项目详情图")
     private String projectDetailImage;
-
-    /** 是否启用(0否1是) */
+    /** 开通理由 */
+    @Excel(name = "开通理由")
+    @ApiModelProperty("开通理由")
+    private String   applyReason;
+    /** 开通理由 */
+    @Excel(name = "驳回原因")
+    @ApiModelProperty("驳回原因")
+    private String   reason;
+    /**
+     * 是否启用(0否1是)
+     */
     @Excel(name = "是否启用(0否1是)")
     @ApiModelProperty("是否启用(0否1是)")
-    private Long projectIsEnable;
-
-    /** 创建人 */
+    private Integer projectIsEnable;
+    /**
+     * 商户ID
+     */
+    @Excel(name = "商户ID")
+    @ApiModelProperty("商户ID")
+    private String merchantId;
+    /**
+     * 审核状态:0-待审核,1-审核通过,2-审核驳回
+     */
+    @Excel(name = "审核状态:0-待审核,1-审核通过,2-审核驳回")
+    @ApiModelProperty("审核状态:0-待审核,1-审核通过,2-审核驳回")
+    private Integer auditStatus;
+    /**
+     * 创建人
+     */
     @Excel(name = "创建人")
     @ApiModelProperty("创建人")
     private Long createUser;
-
-    /** 修改人 */
+    /** 申请时间 */
+    @Excel(name = "申请时间")
+    @ApiModelProperty("申请时间")
+    private Data applyTime;
+    /**
+     * 修改人
+     */
     @Excel(name = "修改人")
     @ApiModelProperty("修改人")
     private Long updateUser;
 
-    /** 是否删除(0否1是) */
+    /**
+     * 是否删除(0否1是)
+     */
     @ApiModelProperty("是否删除(0否1是)")
     @Excel(name = "是否删除(0否1是)")
     private Long isDelete;

+ 25 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTeProject.java

@@ -0,0 +1,25 @@
+package com.ylx.massage.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * 商户服务项目关联对象 ma_te_project
+ */
+@Data
+@TableName("ma_te_project")
+public class MaTeProject {
+
+    /**
+     * 商户ID
+     */
+    @TableField("te_id")
+    private Long teId;
+
+    /**
+     * 项目ID
+     */
+    @TableField("project_id")
+    private Long projectId;
+}

+ 238 - 197
nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTechnician.java

@@ -1,235 +1,276 @@
 package com.ylx.massage.domain;
 
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
 import com.ylx.common.annotation.Excel;
 import com.ylx.common.core.domain.BaseEntity;
 
+import java.util.Date;
+
+
 /**
- * 技师对象 ma_technician
+ * 商户对象
  *
  * @author ylx
  * @date 2024-03-22
  */
-public class MaTechnician extends BaseEntity
-{
+
+@Data
+@TableName("ma_technician")
+@Accessors(chain = true)
+public class MaTechnician extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
-    /** id */
+    /**
+     * id
+     */
+    @TableId(type = IdType.AUTO)
     private Long id;
 
-    /** 姓名 */
+    /**
+     * 姓名
+     */
+    @TableField("te_name")
     @Excel(name = "姓名")
     private String teName;
-
-    /** 性别(0女1男) */
+    /**
+     * 昵称
+     */
+    @Excel(name = "昵称")
+    @TableField("te_nick_name")
+    private String teNickName;
+    /**
+     * 微信小程序登录会话密钥
+     */
+    @TableField("c_session_key")
+    @ApiModelProperty("微信小程序登录会话密钥")
+    private String cSessionKey;
+    /**
+     * 密码
+     */
+    @Excel(name = "密码")
+    @TableField("te_pass_word")
+    private String tePassword;
+    /**
+     * 用户的openId
+     */
+    @TableField("c_openid")
+    @ApiModelProperty("用户的openId")
+    private String cOpenid;
+    /**
+     * 性别(0女1男)
+     */
     @Excel(name = "性别(0女1男)")
-    private Long teSex;
+    @TableField("te_sex")
+    private Integer teSex;
 
-    /** 电话 */
+    /**
+     * 电话
+     */
     @Excel(name = "电话")
+    @TableField("te_phone")
     private String tePhone;
 
-    /** 地址 */
-    @Excel(name = "地址")
+    /**
+     * 合作意向城市
+     */
+    @Excel(name = "合作意向城市")
+    @TableField("te_address")
     private String teAddress;
 
-    /** 年龄 */
+    /**
+     * 城市编码
+     */
+    private String teAreaCode;
+
+    /**
+     * 年龄
+     */
+    /**
+     * 年龄
+     */
     @Excel(name = "年龄")
-    private Long teAge;
+    @TableField("te_age")
+    private Integer teAge;
 
-    /** 头像 */
+    /**
+     * 头像
+     */
     @Excel(name = "头像")
+    @TableField("te_avatar")
     private String teAvatar;
 
-    /** 生活照 */
+    /**
+     * 服务标签
+     */
+    @TableField("service_tag")
+    private Integer serviceTag;
+
+    /**
+     * 开通服务类目ID
+     */
+    @Excel(name = "开通服务类目ID")
+    @TableField("openService")
+    private String openService;
+
+    /**
+     * 可服务项目名称
+     */
+    @TableField("te_project")
+    private String teProject;
+
+    /**
+     * 生活照
+     */
     @Excel(name = "生活照")
+    @TableField("life_photos")
     private String lifePhotos;
 
-    /** 简介 */
+    /**
+     * 形象照
+     */
+    @Excel(name = "形象照")
+    @TableField("avatar")
+    private String avatar;
+
+    /**
+     * 身份证
+     */
+    @Excel(name = "身份证")
+    @TableField("id_card")
+    private String idCard;
+
+    /**
+     * 健康证
+     */
+    @Excel(name = "健康证")
+    @TableField("health_certificate")
+    private String healthCertificate;
+
+    /**
+     * 从业资格证
+     */
+    @Excel(name = "从业资格证")
+    @TableField("qualification_certificate")
+    private String qualificationCertificate;
+
+    /**
+     * 无犯罪证明
+     */
+    @Excel(name = "无犯罪证明")
+    @TableField("no_crime_record")
+    private String noCrimeRecord;
+    /**
+     * 宣传视频
+     */
+    @Excel(name = "宣传视频")
+    @TableField("promo_video")
+    private String promoVideo;
+    /**
+     * 承诺书
+     */
+    @Excel(name = "承诺书")
+    @TableField("commitment_pdf")
+    private String commitmentPdf;
+    /**
+     * 承诺录音
+     */
+    @Excel(name = "承诺录音")
+    @TableField("commitment_audio")
+    private String commitmentAudio;
+    /**
+     * 承诺录像
+     */
+    @Excel(name = "承诺录像")
+    @TableField("commitment_video")
+    private String commitmentVideo;
+
+    /**
+     * 简介
+     */
     @Excel(name = "简介")
+    @TableField("te_brief")
     private String teBrief;
 
-    /** 服务状态(0服务中,1可服务) */
+    /**
+     * 服务状态(0服务中,1可服务)
+     */
     @Excel(name = "服务状态(0服务中,1可服务)")
-    private Long serviceState;
-
-    /** 是否在岗(0否1是) */
-    @Excel(name = "是否在岗(0否1是)")
-    private Long postState;
-
-    /** 是否启用(0否1是) */
+    private Integer serviceState;
+
+    /**
+     * 上岗状态 (-1:未上岗 0:已上岗 1:已申请)
+     */
+    @Excel(name = "上岗状态 (-1:未上岗 0:已上岗 1:已申请)")
+    private Integer nStatus2;
+
+    /**
+     * 商户管理状态: 0-正常, 1-限制接单, 2-冻结, 3-注销
+     */
+    @Excel(name = "商户管理状态: 0-正常, 1-限制接单, 2-冻结, 3-注销")
+    private String merchantStatus;
+    /**
+     * 商户状态(0休息中1在线接单)
+     */
+    @Excel(name = "商户状态(0休息中1在线接单)")
+    private Integer postState;
+
+    /**
+     * 商户类型(0:真实商户 1:虚拟商户)
+     */
+    @Excel(name = "商户类型(0:真实商户 1:虚拟商户)")
+    private Integer techType;
+
+    /**
+     * 是否启用(0否1是)
+     */
     @Excel(name = "是否启用(0否1是)")
-    private Long teIsEnable;
-
-    /** 创建人 */
-    @Excel(name = "创建人")
-    private Long createUser;
-
-    /** 修改人 */
-    @Excel(name = "修改人")
-    private Long updateUser;
-
-    /** 是否删除(0否1是) */
+    private Integer teIsEnable;
+
+    /**
+     * 审核状态:-1-申请入住,0-待入驻,1-待审核,2-审核通过,3-审核驳回
+     */
+    @Excel(name = "审核状态:-1-申请入住,0-待入驻,1-待审核,2-审核通过,3-审核驳回")
+    private Integer auditStatus;
+
+    /**
+     * 审批时间
+     */
+    @Excel(name = "审批时间")
+    private Date approveTime;
+
+    /**
+     * 是否删除(0否1是)
+     */
     @Excel(name = "是否删除(0否1是)")
-    private Long isDelete;
-
-    public void setId(Long id)
-    {
-        this.id = id;
-    }
-
-    public Long getId()
-    {
-        return id;
-    }
-    public void setTeName(String teName)
-    {
-        this.teName = teName;
-    }
-
-    public String getTeName()
-    {
-        return teName;
-    }
-    public void setTeSex(Long teSex)
-    {
-        this.teSex = teSex;
-    }
-
-    public Long getTeSex()
-    {
-        return teSex;
-    }
-    public void setTePhone(String tePhone)
-    {
-        this.tePhone = tePhone;
-    }
-
-    public String getTePhone()
-    {
-        return tePhone;
-    }
-    public void setTeAddress(String teAddress)
-    {
-        this.teAddress = teAddress;
-    }
-
-    public String getTeAddress()
-    {
-        return teAddress;
-    }
-    public void setTeAge(Long teAge)
-    {
-        this.teAge = teAge;
-    }
-
-    public Long getTeAge()
-    {
-        return teAge;
-    }
-    public void setTeAvatar(String teAvatar)
-    {
-        this.teAvatar = teAvatar;
-    }
-
-    public String getTeAvatar()
-    {
-        return teAvatar;
-    }
-    public void setLifePhotos(String lifePhotos)
-    {
-        this.lifePhotos = lifePhotos;
-    }
-
-    public String getLifePhotos()
-    {
-        return lifePhotos;
-    }
-    public void setTeBrief(String teBrief)
-    {
-        this.teBrief = teBrief;
-    }
-
-    public String getTeBrief()
-    {
-        return teBrief;
-    }
-    public void setServiceState(Long serviceState)
-    {
-        this.serviceState = serviceState;
-    }
-
-    public Long getServiceState()
-    {
-        return serviceState;
-    }
-    public void setPostState(Long postState)
-    {
-        this.postState = postState;
-    }
-
-    public Long getPostState()
-    {
-        return postState;
-    }
-    public void setTeIsEnable(Long teIsEnable)
-    {
-        this.teIsEnable = teIsEnable;
-    }
-
-    public Long getTeIsEnable()
-    {
-        return teIsEnable;
-    }
-    public void setCreateUser(Long createUser)
-    {
-        this.createUser = createUser;
-    }
-
-    public Long getCreateUser()
-    {
-        return createUser;
-    }
-    public void setUpdateUser(Long updateUser)
-    {
-        this.updateUser = updateUser;
-    }
-
-    public Long getUpdateUser()
-    {
-        return updateUser;
-    }
-    public void setIsDelete(Long isDelete)
-    {
-        this.isDelete = isDelete;
-    }
-
-    public Long getIsDelete()
-    {
-        return isDelete;
-    }
-
-    @Override
-    public String toString() {
-        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
-            .append("id", getId())
-            .append("teName", getTeName())
-            .append("teSex", getTeSex())
-            .append("tePhone", getTePhone())
-            .append("teAddress", getTeAddress())
-            .append("teAge", getTeAge())
-            .append("teAvatar", getTeAvatar())
-            .append("lifePhotos", getLifePhotos())
-            .append("teBrief", getTeBrief())
-            .append("serviceState", getServiceState())
-            .append("postState", getPostState())
-            .append("teIsEnable", getTeIsEnable())
-            .append("createUser", getCreateUser())
-            .append("updateUser", getUpdateUser())
-            .append("createTime", getCreateTime())
-            .append("updateTime", getUpdateTime())
-            .append("isDelete", getIsDelete())
-            .toString();
-    }
+    private Integer isDelete;
+
+    /**
+     * 评分
+     * 用户对技师的评分,通常为1-5分
+     */
+    @ApiModelProperty("评分")
+    private Integer nStar;
+
+    /**
+     * 已服务数量
+     * 技师累计完成的订单总数
+     */
+    @TableField("n_num")
+    @ApiModelProperty("已服务订单数量")
+    private Integer nNum;
+
+    /**
+     * 是否推荐(0否1是)
+     */
+    @Excel(name = "是否推荐(0否1是)")
+    @TableField("is_recommend")
+    @ApiModelProperty("是否推荐")
+    private Integer isRecommend;
+
 }

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

@@ -26,6 +26,12 @@ public class TAddress extends Model<TAddress> {
     @ApiModelProperty("主键")
     private String id;
 
+    /**
+     * 商户id
+     */
+    @ApiModelProperty("商户id")
+    private Long merchantId;
+
     /**
      * 用户openId
      */

+ 20 - 0
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.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -12,6 +13,7 @@ import lombok.Setter;
 import lombok.experimental.Accessors;
 
 import java.io.Serializable;
+import java.util.Date;
 
 /**
  * <p>
@@ -46,6 +48,16 @@ public class TLbt implements Serializable {
     @JsonProperty("cImgUrl")
     private String cImgUrl;
 
+    @TableField("c_jump_url")
+    @ApiModelProperty("跳转地址")
+    @JsonProperty("cJumpUrl")
+    private String cJumpUrl;
+
+    @TableField("c_jump_type")
+    @ApiModelProperty("跳转类型")
+    @JsonProperty("cJumpType")
+    private Integer cJumpType;
+
     /**
      * 描述
      */
@@ -70,6 +82,11 @@ public class TLbt implements Serializable {
     @JsonProperty("cSort")
     private Integer cSort;
 
+    @TableField("c_status")
+    @ApiModelProperty("显示状态: 0-隐藏, 1-显示")
+    @JsonProperty("cStatus")
+    private String cStatus;
+
     /**
      * 是否删除 0否1是
      */
@@ -79,4 +96,7 @@ public class TLbt implements Serializable {
     @JsonProperty("isDelete")
     private Integer isDelete;
 
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
 }

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

@@ -1,863 +0,0 @@
-package com.ylx.massage.domain;
-
-import com.alibaba.fastjson.JSONArray;
-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.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.ylx.common.annotation.Excel;
-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.LocalDateTime;
-import java.util.Date;
-
-/**
- * 订单实体类
- * <p>
- * 对应数据库表 t_order,用于存储按摩服务订单的完整信息。
- * 包含订单基本信息、服务项目信息、用户信息、技师信息、支付信息、
- * 状态流转信息等多个维度的业务数据。
- *
- * 订单状态流转:
- * -1(待付款) → 0(待接单) → 1(已接单) → 6(已出发) → 2(已到达) → 3(服务中) → 4(待评价) → 5(已完成)
- * 特殊状态:-2(已取消)、-3(已拒绝)
- * </p>
- *
- * @author YJR
- * @version 1.0
- * @since 2023-12-30
- */
-@Getter
-@Setter
-@Accessors(chain = true)
-@TableName(value = "t_order", autoResultMap = true)
-@ApiModel(value = "TOrder", description = "订单实体类")
-public class TOrder implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 订单ID
-     * 主键,唯一标识订单记录
-     */
-    @TableId("c_id")
-    @ApiModelProperty("订单ID")
-    private String cId;
-
-    /**
-     * 订单号
-     * 系统生成的唯一订单编号,用于订单查询和标识
-     */
-    @Excel(name = "订单号")
-    @TableField("order_no")
-    @ApiModelProperty("订单号")
-    private String orderNo;
-
-    /**
-     * 父订单号
-     * 用于升级订单场景,记录原订单号
-     */
-    @TableField("parent_no")
-    @ApiModelProperty("父订单号")
-    private String parentNo;
-
-    /**
-     * 支付类型
-     * 1-微信支付 2-余额支付 3-现金支付
-     */
-    @TableField("pay_type")
-    @ApiModelProperty("支付类型:1微信支付 2:余额支付 3:现金支付")
-    private Integer payType;
-
-    /**
-     * 订单类型
-     * 0-普通订单 1-加钟订单 2-升级订单
-     */
-    @TableField("order_type")
-    @ApiModelProperty("订单类型:0普通订单,1加钟订单,2升级订单")
-    private Integer orderType;
-
-    /**
-     * 是否为虚拟技师订单
-     * 0-否 1-是
-     */
-    @TableField("virtual_order_flag")
-    @ApiModelProperty("是否为虚拟技师订单:0否,1是")
-    private Integer virtualOrderFlag;
-
-    /**
-     * 虚拟技师订单分配情况(0:真实订单 1:未分配 2:已分配)
-     */
-    @TableField("virtual_order_allocation")
-    @ApiModelProperty("虚拟技师订单分配情况(0:真实订单 1:未分配 2:已分配)")
-    private Integer virtualOrderAllocation;
-
-    /**
-     * 技师ID
-     */
-    @TableField("c_js_id")
-    @ApiModelProperty("技师ID")
-    private String cJsId;
-
-    /**
-     * 老技师ID
-     * 用于记录原技师信息,在换技师场景下使用
-     */
-    @TableField("old_js_id")
-    @ApiModelProperty("老技师ID")
-    private String oldJsId;
-
-    /**
-     * 超时原因
-     * 订单超时的原因说明
-     */
-    @TableField("timeout_cause")
-    @ApiModelProperty("超时原因")
-    private String timeoutCause;
-
-    /**
-     * 音频
-     * 订单相关的音频文件路径
-     */
-    @TableField("tape")
-    @ApiModelProperty("音频")
-    private String tape;
-
-    /**
-     * 车费
-     * 根据距离和时段计算的车费金额
-     */
-    @TableField("fare")
-    @ApiModelProperty("车费")
-    private BigDecimal fare;
-
-    /**
-     * 距离
-     * 技师与用户之间的距离,单位:公里
-     */
-    @TableField("distance")
-    @ApiModelProperty("距离")
-    private BigDecimal distance;
-
-
-    /**
-     * 优惠金额
-     * 使用优惠券或其他优惠减免的金额
-     */
-    @TableField("preferential")
-    @ApiModelProperty("优惠金额")
-    private BigDecimal preferential;
-
-    /**
-     * 差价
-     * 升级订单时,需要补交的差价金额
-     */
-    @TableField("price_difference")
-    @ApiModelProperty("差价")
-    private BigDecimal priceDifference;
-
-
-
-    /**
-     * 用户OpenID
-     * 微信用户的唯一标识
-     */
-    @TableField("c_open_id")
-    @ApiModelProperty("用户OpenID")
-    private String cOpenId;
-
-    /**
-     * 经度
-     * 用户地址的经度坐标
-     */
-    @ApiModelProperty("经度")
-    private Double longitude;
-
-    /**
-     * 纬度
-     * 用户地址的纬度坐标
-     */
-    @ApiModelProperty("纬度")
-    private Double latitude;
-
-    /**
-     * 出发经度
-     * 技师出发位置的经度
-     */
-    @ApiModelProperty("出发经度")
-    private BigDecimal departLongitude;
-
-    /**
-     * 出发纬度
-     * 技师出发位置的纬度
-     */
-    @ApiModelProperty("出发纬度")
-    private BigDecimal departLatitude;
-
-    /**
-     * 到达经度
-     * 技师到达用户位置的经度
-     */
-    @ApiModelProperty("到达经度")
-    private BigDecimal arrivalLongitude;
-
-    /**
-     * 到达纬度
-     * 技师到达用户位置的纬度
-     */
-    @ApiModelProperty("到达纬度")
-    private BigDecimal arrivalLatitude;
-
-    /**
-     * 到达照片
-     * 技师到达现场后拍摄的照片URL
-     */
-    @ApiModelProperty("到达照片")
-    private String arrivalPhoto;
-
-    /**
-     * 出发时间
-     * 技师出发前往用户地址的时间
-     */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
-    @ApiModelProperty("出发时间")
-    private Date departTime;
-
-    /**
-     * 支付时间
-     * 用户完成支付的时间
-     */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
-    @ApiModelProperty("支付时间")
-    private Date payTime;
-
-    /**
-     * 详细地址
-     * 用户提供的服务地址
-     */
-    @ApiModelProperty("详细地址")
-    private String address;
-
-    /**
-     * 地址名称
-     * 地址的别名或名称标识
-     */
-    @ApiModelProperty("地址名称")
-    private String name;
-
-    /**
-     * 技师的接单时间
-     */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
-    @ApiModelProperty("接单时间")
-    private LocalDateTime acceptanceTime;
-
-    /**
-     * 到达时间
-     * 技师到达用户地址的时间
-     */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
-    @ApiModelProperty("到达时间")
-    private LocalDateTime reachTime;
-
-    /**
-     * 开始服务时间
-     * 技师开始提供服务的时间
-     */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
-    @ApiModelProperty("开始服务时间")
-    private LocalDateTime startTime;
-
-    /**
-     * 开始服务时间字符串
-     */
-    @TableField(exist = false)
-    private String startTimeStr;
-
-    /**
-     * 结束时间
-     * 技师完成服务的时间
-     */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
-    @ApiModelProperty("结束时间")
-    private LocalDateTime endTime;
-
-    @TableField(exist = false)
-    private String endTimeStr;
-
-    /**
-     * 拒接原因
-     * 技师拒绝接单的原因说明
-     */
-    @ApiModelProperty("拒接原因")
-    private String reasonRefusal;
-
-    /**
-     * 领取优惠券ID
-     */
-    @TableField("coupon_receive_id")
-    @ApiModelProperty("领取优惠券ID")
-    private String couponReceiveId;
-
-    /**
-     * 订单明细
-     * JSON格式,包含服务项目的详细信息
-     */
-    @TableField(value = "c_goods", typeHandler = FastjsonTypeHandler.class)
-    @ApiModelProperty("订单明细")
-    private JSONArray cGoods;
-
-    /**
-     * 订单金额
-     * 服务项目的基础金额总和
-     */
-    @TableField("d_total_money")
-    @ApiModelProperty("订单金额")
-    private BigDecimal dTotalMoney;
-
-    /**
-     * 总价
-     * 订单最终金额 = 订单金额 + 车费 - 优惠金额
-     */
-    @Excel(name = "订单金额")
-    @TableField("total_price")
-    @ApiModelProperty("总价")
-    private BigDecimal totalPrice;
-
-    /**
-     * 订单状态
-     * -1-待付款 0-待接单 1-已接单 2-已到达 3-服务中 4-待评价(已完成)
-     * 5-已完成(已评价) 6-已出发 -2-已取消 -3-已拒绝
-     * 7:退单待审核 8:退单审核通过
-     */
-    @TableField("n_status")
-    @ApiModelProperty("订单状态: -1待付款 0待接单 1已接单 6已出发 2已到达 3服务中 4待评价 5已完成 7:退单待审核 8:退单审核通过  -2已取消 -3已拒绝")
-    private Integer nStatus;
-
-    /**
-     * 2026-03-27 新增字段
-     * 订单待结算状态
-     * -0-未结算 1-待结算 2-已结算
-     */
-    @TableField("w_status")
-    @ApiModelProperty("待结算状态: -0-未结算(默认) 1-待结算 2-已结算")
-    private Integer wStatus;
-
-    /**
-     * 备注地址
-     * 地址的补充说明或地标信息
-     */
-    @ApiModelProperty("备注地址")
-    private String atlasAdd;
-
-    /**
-     * 地址
-     * 用户提供的服务地址(冗余字段)
-     */
-    @TableField("c_address")
-    @ApiModelProperty("地址")
-    private String cAddress;
-
-
-    /**
-     * 用户姓名
-     * 下单用户的真实姓名
-     */
-    @Excel(name = "用户姓名")
-    @TableField("c_name")
-    @ApiModelProperty("用户姓名")
-    private String cName;
-
-    /**
-     * 用户电话
-     * 下单用户的联系电话
-     */
-    @Excel(name = "用户电话")
-    @TableField("c_phone")
-    @ApiModelProperty("用户电话")
-    private String cPhone;
-
-    /**
-     * 备注
-     * 用户填写的订单备注信息
-     */
-    @TableField("c_note")
-    @ApiModelProperty("备注")
-    private String cNote;
-
-    /**
-     * 支付截止时间
-     * 订单需要完成支付的截止时间
-     */
-    @TableField("c_time")
-    @ApiModelProperty("支付截止时间")
-    private String cTime;
-
-    /**
-     * 推荐者ID
-     * 推荐用户的OpenID,用于分销统计
-     */
-    @TableField("c_tj_open_id")
-    @ApiModelProperty("推荐者ID")
-    private String cTjOpenId;
-
-    /**
-     * 部门ID
-     * 订单所属的部门/城市标识
-     */
-    @TableField("dept_id")
-    @ApiModelProperty("部门ID")
-    private String deptId;
-
-    /**
-     * 部门名称
-     * 订单所属的城市名称
-     */
-    @TableField("dept_name")
-    @ApiModelProperty("部门名称")
-    private String deptName;
-
-    /**
-     * 创建时间
-     * 订单创建的时间/提交时间
-     */
-    @Excel(name="提交时间")
-    @TableField("dt_create_time")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
-    @ApiModelProperty("创建时间/提交时间")
-    private LocalDateTime dtCreateTime;
-
-    /**
-     * 逻辑删除标记
-     * 0-未删除(有效记录) 1-已删除
-     */
-    @ApiModelProperty("是否删除:0否1是")
-    @TableLogic
-    private Integer isDelete;
-
-
-    /**
-     * 技师信息
-     * 非数据库字段,用于关联查询技师信息
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("技师信息")
-    private TJs js;
-
-    /**
-     * 老技师信息
-     * 非数据库字段,用于关联查询原技师信息
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("老技师信息")
-    private TJs oldJs;
-
-    /**
-     * 剩余支付时间
-     * 非数据库字段,用于计算订单支付的剩余时间(秒)
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("剩余支付时间(秒)")
-    private Long remainingTime;
-
-    /**
-     * 技师姓名
-     * 非数据库字段,用于关联查询技师姓名
-     */
-    @Excel(name = "商户名称")
-    @TableField(exist = false)
-    @ApiModelProperty("技师姓名")
-    private String jsName;
-
-    /**
-     * 技师昵称
-     * 非数据库字段,用于关联查询技师昵称
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("技师昵称")
-    @JsonProperty("cNickName")
-    private String cNickName;
-
-    /**
-     * 技师电话
-     * 非数据库字段,用于关联查询技师电话
-     */
-    @Excel(name = "技师电话")
-    @TableField(exist = false)
-    @ApiModelProperty("技师电话")
-    private String jsPhone;
-
-    /**
-     * 用户信息
-     * 非数据库字段,用于关联查询微信用户信息
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("用户信息")
-    private TWxUser wxUser;
-
-    /**
-     * 订单状态名称
-     * 非数据库字段,用于展示订单状态的文本描述
-     */
-    @TableField(exist = false)
-    @Excel(name = "订单状态名称")
-    @ApiModelProperty("订单状态名称")
-    private String statusName;
-
-    /**
-     * 订单金额
-     * 非数据库字段,用于展示订单金额的文本描述
-     */
-    @TableField(exist = false)
-    @Excel(name = "订单金额")
-    @ApiModelProperty("订单金额")
-    private String orderPrice;
-
-    /**
-     * 实付金额
-     * 非数据库字段,用于展示实付金额的文本描述
-     */
-    @TableField(exist = false)
-    @Excel(name = "实付金额")
-    @ApiModelProperty("实付金额")
-    private String realPayPrice;
-
-    /**
-     * 优惠券补贴
-     * 非数据库字段,用于展示优惠券补贴的文本描述
-     */
-    @TableField(exist = false)
-    @Excel(name = "优惠券补贴")
-    @ApiModelProperty("优惠券补贴")
-    private String couponPrice;
-
-    /**
-     * 平台佣金比例
-     * 非数据库字段,用于展示平台佣金比例的文本描述
-     */
-    @TableField(exist = false)
-    @Excel(name = "平台佣金比例")
-    @ApiModelProperty("平台佣金比例")
-    private String percent;
-
-    /**
-     * 平台佣金
-     * 非数据库字段,用于展示平台佣金的文本描述
-     */
-    @TableField(exist = false)
-    @Excel(name = "平台佣金")
-    @ApiModelProperty("平台佣金")
-    private String yPrice;
-
-    /**
-     * 商户应收金额/待结算金额
-     * 非数据库字段,用于展示商户应收金额的文本描述
-     */
-    @TableField(exist = false)
-    @Excel(name = "商户应收金额")
-    @ApiModelProperty("商户应收金额/待结算金额")
-    private String rPrice;
-
-    /**
-     * 提交时间/订单创建时间
-     * 非数据库字段,用于展示接单时间的文本描述
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("订单创建时间开始")
-    private String dtCreateTimeBegin;
-
-    /**
-     * 订单创建时间
-     * 非数据库字段,用于展示订单创建时间的文本描述
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("订单创建时间结束")
-    private String dtCreateTimeTimeEnd;
-
-    /**
-     * 支付时间
-     * 非数据库字段,用于展示支付时间的文本描述
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("支付时间开始")
-    private String payTimeBegin;
-
-    /**
-     * 支付时间
-     * 非数据库字段,用于展示支付时间的文本描述
-     */
-    @TableField(exist = false)
-    @ApiModelProperty("支付时间结束")
-    private String payTimeEnd;
-
-    /**
-     * 订单服务项目
-     * 非数据库字段,用于展示订单服务项目的文本描述
-     */
-    @Excel(name = "项目名称")
-    @TableField(exist = false)
-    @ApiModelProperty("订单服务项目")
-    private String projectNames;
-
-
-    /**
-     * 服务时长(分钟)
-     */
-    @TableField(exist = false)
-    private String serviceDuration;
-
-    public String getcId() {
-        return cId;
-    }
-
-    public void setcId(String cId) {
-        this.cId = cId;
-    }
-
-    public String getcJsId() {
-        return cJsId;
-    }
-
-    public void setcJsId(String cJsId) {
-        this.cJsId = cJsId;
-    }
-
-    public String getcOpenId() {
-        return cOpenId;
-    }
-
-    public void setcOpenId(String cOpenId) {
-        this.cOpenId = cOpenId;
-    }
-
-    public void setDepartLongitude(BigDecimal departLongitude) {
-        this.departLongitude = departLongitude;
-    }
-
-    public BigDecimal getDepartLongitude() {
-        return departLongitude;
-    }
-
-    public void setArrivalLongitude(BigDecimal arrivalLongitude) {
-        this.arrivalLongitude = arrivalLongitude;
-    }
-
-    public BigDecimal getArrivalLongitude() {
-        return arrivalLongitude;
-    }
-
-    public void setArrivalPhoto(String arrivalPhoto) {
-        this.arrivalPhoto = arrivalPhoto;
-    }
-
-    public String getArrivalPhoto() {
-        return arrivalPhoto;
-    }
-
-    public void setDepartTime(Date departTime) {
-        this.departTime = departTime;
-    }
-
-    public Date getDepartTime() {
-        return departTime;
-    }
-
-    public void setDepartLatitude(BigDecimal departLatitude) {
-        this.departLatitude = departLatitude;
-    }
-
-    public BigDecimal getDepartLatitude() {
-        return departLatitude;
-    }
-
-    public void setArrivalLatitude(BigDecimal arrivalLatitude) {
-        this.arrivalLatitude = arrivalLatitude;
-    }
-
-    public BigDecimal getArrivalLatitude() {
-        return arrivalLatitude;
-    }
-
-    public JSONArray getcGoods() {
-        return cGoods;
-    }
-
-    public void setcGoods(JSONArray cGoods) {
-        this.cGoods = cGoods;
-    }
-
-    public BigDecimal getdTotalMoney() {
-        return dTotalMoney;
-    }
-
-    public void setdTotalMoney(BigDecimal dTotalMoney) {
-        this.dTotalMoney = dTotalMoney;
-    }
-
-    public Integer getnStatus() {
-        return nStatus;
-    }
-
-    public void setnStatus(Integer nStatus) {
-        this.nStatus = nStatus;
-    }
-
-    public String getcAddress() {
-        return cAddress;
-    }
-
-    public void setcAddress(String cAddress) {
-        this.cAddress = cAddress;
-    }
-
-    public String getcName() {
-        return cName;
-    }
-
-    public void setcName(String cName) {
-        this.cName = cName;
-    }
-
-    public String getcPhone() {
-        return cPhone;
-    }
-
-    public void setcPhone(String cPhone) {
-        this.cPhone = cPhone;
-    }
-
-    public String getcNote() {
-        return cNote;
-    }
-
-    public void setcNote(String cNote) {
-        this.cNote = cNote;
-    }
-
-    public String getcTime() {
-        return cTime;
-    }
-
-    public void setcTime(String cTime) {
-        this.cTime = cTime;
-    }
-
-    public String getcTjOpenId() {
-        return cTjOpenId;
-    }
-
-    public void setcTjOpenId(String cTjOpenId) {
-        this.cTjOpenId = cTjOpenId;
-    }
-
-    public LocalDateTime getDtCreateTime() {
-        return dtCreateTime;
-    }
-
-    public void setDtCreateTime(LocalDateTime dtCreateTime) {
-        this.dtCreateTime = dtCreateTime;
-    }
-
-    public TJs getJs() {
-        return js;
-    }
-
-    public void setJs(TJs js) {
-        this.js = js;
-    }
-
-    public TWxUser getWxUser() {
-        return wxUser;
-    }
-
-    public void setWxUser(TWxUser wxUser) {
-        this.wxUser = wxUser;
-    }
-
-    public Integer getwStatus() {
-        return wStatus;
-    }
-
-    public void setwStatus(Integer wStatus) {
-        this.wStatus = wStatus;
-    }
-
-    public String getOrderPrice() {
-        return orderPrice;
-    }
-
-    public void setOrderPrice(String orderPrice) {
-        this.orderPrice = orderPrice;
-    }
-
-    public String getRealPayPrice() {
-        return realPayPrice;
-    }
-
-    public void setRealPayPrice(String realPayPrice) {
-        this.realPayPrice = realPayPrice;
-    }
-
-    public String getCouponPrice() {
-        return couponPrice;
-    }
-
-    public void setCouponPrice(String couponPrice) {
-        this.couponPrice = couponPrice;
-    }
-
-    public String getPercent() {
-        return percent;
-    }
-
-    public void setPercent(String percent) {
-        this.percent = percent;
-    }
-
-    public String getyPrice() {
-        return yPrice;
-    }
-
-    public void setyPrice(String yPrice) {
-        this.yPrice = yPrice;
-    }
-
-    public String getrPrice() {
-        return rPrice;
-    }
-
-    public void setrPrice(String rPrice) {
-        this.rPrice = rPrice;
-    }
-
-    public String getProjectNames() {
-        return projectNames;
-    }
-
-    public void setProjectNames(String projectNames) {
-        this.projectNames = projectNames;
-    }
-
-    public String getDtCreateTimeBegin() {
-        return dtCreateTimeBegin;
-    }
-
-    public void setDtCreateTimeBegin(String dtCreateTimeBegin) {
-        this.dtCreateTimeBegin = dtCreateTimeBegin;
-    }
-
-    public String getDtCreateTimeTimeEnd() {
-        return dtCreateTimeTimeEnd;
-    }
-
-    public void setDtCreateTimeTimeEnd(String dtCreateTimeTimeEnd) {
-        this.dtCreateTimeTimeEnd = dtCreateTimeTimeEnd;
-    }
-}

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

@@ -84,6 +84,13 @@ public class TWxUser implements Serializable {
     @ApiModelProperty("昵称")
     private String cNickName;
 
+    /**
+     * 密码
+     */
+    @TableField("c_password")
+    @ApiModelProperty("密码")
+    private String cPassword;
+
     /**
      * 消费金额
      */
@@ -106,10 +113,10 @@ public class TWxUser implements Serializable {
     private JSONArray cAddressList;
 
     /**
-     * 当前余额
+     * 当前购物金剩余(原余额
      */
     @TableField("d_balance")
-    @ApiModelProperty("当前余")
+    @ApiModelProperty("当前购物金剩余")
     private BigDecimal dBalance;
 
     /**

+ 26 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaProjectSaveDto.java

@@ -0,0 +1,26 @@
+package com.ylx.massage.domain.dto;
+
+import com.ylx.point.enums.TaskLimitTimesEnum;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class MaProjectSaveDto {
+    /**
+     * 商户id
+     */
+    private String userId;
+    /**
+     * 商户手机号
+     */
+    private String merchantPhone;
+    /**
+     * 项目id列表
+     */
+    private List<String> projectIdList;
+    /**
+     * 申请理由
+     */
+    private String applyReason;
+}

+ 31 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaProjectUpdateDto.java

@@ -0,0 +1,31 @@
+package com.ylx.massage.domain.dto;
+
+import com.ylx.common.annotation.Excel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class MaProjectUpdateDto {
+
+    /**
+     * 项目id列表
+     */
+    private String projectId;
+    /**
+     * 申请理由
+     */
+    private String applyReason;
+    /**
+     * 是否下架
+     */
+    private Boolean isPass;
+    /**
+     * 是否删除
+     */
+    private Boolean isDelete;
+    /** 我的售价 */
+    private BigDecimal projectCurrentPrice;
+}

+ 61 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaTechnicianAuditQueryDTO.java

@@ -0,0 +1,61 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 后台商户入驻审核查询参数。
+ */
+@Data
+@ApiModel(value = "MaTechnicianAuditQueryDTO", description = "后台商户入驻审核查询参数")
+public class MaTechnicianAuditQueryDTO {
+
+    /**
+     * 商户姓名,支持模糊查询。
+     */
+    @ApiModelProperty("姓名")
+    private String teName;
+
+    /**
+     * 商户昵称,支持模糊查询。
+     */
+    @ApiModelProperty("昵称")
+    private String teNickName;
+
+    /**
+     * 用户类型:0-正式用户,1-虚拟用户。
+     */
+    @ApiModelProperty("用户类型:0-正式用户 1-虚拟用户")
+    private Integer techType;
+
+    /**
+     * 商户电话,支持模糊查询。
+     */
+    @ApiModelProperty("商户电话")
+    private String tePhone;
+
+    /**
+     * 申请开始时间,格式:yyyy-MM-dd HH:mm:ss。
+     */
+    @ApiModelProperty("申请开始时间")
+    private String beginApplyTime;
+
+    /**
+     * 申请结束时间,格式:yyyy-MM-dd HH:mm:ss。
+     */
+    @ApiModelProperty("申请结束时间")
+    private String endApplyTime;
+
+    /**
+     * 性别:0-女,1-男。
+     */
+    @ApiModelProperty("性别:0-女 1-男")
+    private Integer teSex;
+
+    /**
+     * 审核状态:0-待入驻,1-待审核,2-审核通过,3-审核驳回。
+     */
+    @ApiModelProperty("审核状态:0-待入驻 1-待审核 2-审核通过 3-审核驳回")
+    private Integer auditStatus;
+}

+ 63 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaTechnicianMerchantAddDTO.java

@@ -0,0 +1,63 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 后台新增商户请求参数
+ */
+@ApiModel(value = "MaTechnicianMerchantAddDTO", description = "后台新增商户请求参数")
+@Data
+public class MaTechnicianMerchantAddDTO {
+
+    /**
+     * 商户真实姓名。
+     */
+    @ApiModelProperty("姓名")
+    private String teName;
+
+    /**
+     * 商户在平台对外展示的昵称。
+     */
+    @ApiModelProperty("昵称")
+    private String teNickName;
+
+    /**
+     * 商户性别:0-女,1-男。
+     */
+    @ApiModelProperty("性别(0女1男)")
+    private Integer teSex;
+
+    /**
+     * 商户联系电话。
+     */
+    @ApiModelProperty("电话")
+    private String tePhone;
+
+    /**
+     * 商户开通的服务类目ID集合。
+     */
+    @ApiModelProperty("服务类目ID集合")
+    private List<Integer> openService;
+
+    /**
+     * 商户开通的服务项目ID集合。
+     */
+    @ApiModelProperty("服务项目ID集合")
+    private List<Long> projectIds;
+
+    /**
+     * 商户类型:0-真实商户,1-虚拟商户。
+     */
+    @ApiModelProperty("商户类型(0:真实商户 1:虚拟商户)")
+    private Integer techType;
+
+    /**
+     * 首页是否推荐:0-否,1-是
+     */
+    @ApiModelProperty("是否推荐(0否1是)")
+    private Integer isRecommend;
+}

+ 73 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MaTechnicianMerchantQueryDTO.java

@@ -0,0 +1,73 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 后台商户列表查询参数
+ */
+@ApiModel(value = "MaTechnicianMerchantQueryDTO", description = "后台商户列表查询参数")
+@Data
+public class MaTechnicianMerchantQueryDTO {
+
+    /**
+     * 商户姓名,支持模糊查询。
+     */
+    @ApiModelProperty("姓名")
+    private String teName;
+
+    /**
+     * 商户昵称,支持模糊查询。
+     */
+    @ApiModelProperty("昵称")
+    private String teNickName;
+
+    /**
+     * 服务类目ID
+     */
+    @ApiModelProperty("服务类目ID")
+    private Integer openService;
+
+    /**
+     * 商户电话,支持模糊查询。
+     */
+    @ApiModelProperty("商户电话")
+    private String tePhone;
+
+    /**
+     * 注册开始时间,格式:yyyy-MM-dd HH:mm:ss。
+     */
+    @ApiModelProperty("注册开始时间")
+    private String beginCreateTime;
+
+    /**
+     * 注册结束时间,格式:yyyy-MM-dd HH:mm:ss。
+     */
+    @ApiModelProperty("注册结束时间")
+    private String endCreateTime;
+
+    /**
+     * 上架状态:0-下架,1-上架。
+     */
+    @ApiModelProperty("上架状态:0-下架 1-上架")
+    private Integer postState;
+
+    /**
+     * 性别:0-女,1-男。
+     */
+    @ApiModelProperty("性别:0-女 1-男")
+    private Integer teSex;
+
+    /**
+     * 服务状态:0-服务中,1-待接单,2-休息中。
+     */
+    @ApiModelProperty("服务状态:0-服务中 1-待接单 2-休息中")
+    private Integer serviceState;
+
+    /**
+     * 用户类型:0-正式用户,1-虚拟用户。
+     */
+    @ApiModelProperty("用户类型:0-正式用户 1-虚拟用户")
+    private Integer techType;
+}

+ 55 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MassageAllMerchantsDto.java

@@ -0,0 +1,55 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+/**
+ * 类描述:首页按摩商户推荐点击全部返回所有按摩商户Dto
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/4 9:31
+ */
+@Data
+public class MassageAllMerchantsDto {
+
+    @NotNull(message = "页码不能为空")
+    @ApiModelProperty("页码")
+    private Integer pageNo;
+
+    @NotNull(message = "每页大小不能为空")
+    @ApiModelProperty("每页大小")
+    private Integer pageSize;
+    /**
+     * 经度
+     * 用户当前位置的经度坐标
+     */
+    @ApiModelProperty("用户经度")
+    private BigDecimal longitude;
+
+    /**
+     * 纬度
+     * 用户当前位置的纬度坐标
+     */
+    @ApiModelProperty("用户纬度")
+    private BigDecimal latitude;
+
+    /**
+     * 城市编码
+     */
+    @NotBlank(message = "城市编码不能为空")
+    @ApiModelProperty("城市编码")
+    private String cityCode;
+
+    /**
+     * 项目id
+     */
+    @ApiModelProperty("项目id")
+    private String projectId;
+
+
+}

+ 38 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MassageMerchantRecommendDto.java

@@ -0,0 +1,38 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.math.BigDecimal;
+
+/**
+ * 类描述:按摩推荐dto
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/2 16:41
+ */
+@Data
+public class MassageMerchantRecommendDto {
+    /**
+     * 经度
+     * 用户当前位置的经度坐标
+     */
+    @ApiModelProperty("用户经度")
+    private BigDecimal longitude;
+
+    /**
+     * 纬度
+     * 用户当前位置的纬度坐标
+     */
+    @ApiModelProperty("用户纬度")
+    private BigDecimal latitude;
+
+    /**
+     * 城市编码
+     */
+    @NotBlank(message = "城市编码不能为空")
+    @ApiModelProperty("城市编码")
+    private String cityCode;
+}

+ 41 - 41
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/CancelOrderApplicationDetailVo.java

@@ -1,7 +1,7 @@
 package com.ylx.massage.domain.vo;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.ylx.massage.domain.TOrder;
+import com.ylx.order.domain.TOrder;
 import lombok.Data;
 
 import java.math.BigDecimal;
@@ -12,44 +12,44 @@ import java.math.BigDecimal;
 @Data
 public class CancelOrderApplicationDetailVo extends TOrder {
 
-    /**
-     * 退单申请ID
-     */
-    @JsonProperty(index = 0)
-    private String id;
-
-    /**
-     * 退单原因(申请备注)
-     */
-    private String cancelOrderReason;
-
-    /**
-     * 项目名称
-     */
-     private String projectName;
-
-    /**
-     * 项目时长
-     */
-     private Integer projectDuration;
-
-    /**
-     * 服务时长(分钟)
-     */
-     //private Integer serviceDuration;
-
-    /**
-     * 项目金额
-     */
-     private BigDecimal projectPrice;
-
-    /**
-     * 退款金额
-     */
-     private BigDecimal refundAmount;
-
-    /**
-     * 审核备注
-     */
-    private String auditRemark;
+//    /**
+//     * 退单申请ID
+//     */
+//    @JsonProperty(index = 0)
+//    private Long id;
+//
+//    /**
+//     * 退单原因(申请备注)
+//     */
+//    private String cancelOrderReason;
+//
+//    /**
+//     * 项目名称
+//     */
+//     private String projectName;
+//
+//    /**
+//     * 项目时长
+//     */
+//     private Integer projectDuration;
+//
+//    /**
+//     * 服务时长(分钟)
+//     */
+//     //private Integer serviceDuration;
+//
+//    /**
+//     * 项目金额
+//     */
+//     private BigDecimal projectPrice;
+//
+//    /**
+//     * 退款金额
+//     */
+//     private BigDecimal refundAmount;
+//
+//    /**
+//     * 审核备注
+//     */
+//    private String auditRemark;
 }

+ 19 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaProjectGetVo.java

@@ -0,0 +1,19 @@
+package com.ylx.massage.domain.vo;
+
+import lombok.Data;
+
+@Data
+public class MaProjectGetVo {
+    /**
+     *审核状态:0-待审核,1-审核通过,2-审核驳回
+     */
+    private String auditStatus;
+    /**
+     *用户ID
+     */
+    private String userId;
+    /**
+     *项目ID
+     */
+    private String typeId;
+}

+ 127 - 16
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianAppAddVo.java

@@ -1,10 +1,14 @@
 package com.ylx.massage.domain.vo;
 
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.ylx.common.annotation.Excel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.experimental.Accessors;
 
+import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Date;
 
 /**
  * 技师对象 ma_technician
@@ -13,53 +17,160 @@ import java.util.ArrayList;
  * @date 2024-03-22
  */
 @Data
-public class MaTechnicianAppAddVo
-{
+public class MaTechnicianAppAddVo {
     private static final long serialVersionUID = 1L;
 
-    /** id */
+    /**
+     * id
+     */
     @ApiModelProperty("id")
-    private Long id;
+    private String id;
 
-    /** 姓名 */
+    /**
+     * 姓名
+     */
     @ApiModelProperty("姓名")
     private String teName;
+    /**
+     * 昵称
+     */
+    @Excel(name = "昵称")
+    private String teNickName;
+    /**
+     * 密码
+     */
+    @Excel(name = "密码")
+    private String tePassword;
 
-    /** 性别(0女1男) */
+    /**
+     * 性别(0女1男)
+     */
     @Excel(name = "性别(0女1男)")
     @ApiModelProperty("性别(0女1男)")
-    private Long teSex;
+    private Integer teSex;
 
-    /** 电话 */
+    /**
+     * 开通服务:1-上门按摩 2-同城玩乐
+     */
+    @Excel(name = "开通服务:1-上门按摩 2-同城玩乐")
+    private BigInteger openService;
+    /**
+     * 电话
+     */
     @Excel(name = "电话")
     @ApiModelProperty("电话")
     private String tePhone;
 
-    /** 地址 */
-    @Excel(name = "地址")
-    @ApiModelProperty("地址")
+    /**
+     * 合作意向城市
+     */
+    @Excel(name = "合作意向城市")
+    @ApiModelProperty("合作意向城市")
     private String teAddress;
 
-    /** 年龄 */
+    /**
+     * 服务标签(1:按摩推拿 2:陪玩)
+     */
+    private Integer serviceTag;
+
+    /**
+     * 年龄
+     */
     @Excel(name = "年龄")
     @ApiModelProperty("年龄")
-    private Long teAge;
+    private BigInteger teAge;
 
-    /** 头像 */
+    /**
+     * 头像
+     */
     @Excel(name = "头像")
     @ApiModelProperty("头像")
     private String teAvatar;
 
-    /** 生活照 */
+    /**
+     * 生活照
+     */
     @Excel(name = "生活照")
     @ApiModelProperty("生活照")
     private String lifePhotos;
 
-    /** 简介 */
+    /**
+     * 简介
+     */
     @Excel(name = "简介")
     @ApiModelProperty("简介")
     private String teBrief;
 
+    /**
+     * 形象照
+     */
+    @Excel(name = "形象照")
+    @ApiModelProperty("形象照")
+    private String avatar;
+
+    /**
+     * 身份证
+     */
+    @Excel(name = "身份证")
+    @ApiModelProperty("身份证")
+    private String idCard;
+    /**
+     * 宣传视频
+     */
+    @Excel(name = "宣传视频")
+    @ApiModelProperty("宣传视频")
+    private String promoVideo;
+    /**
+     * 健康证
+     */
+    @Excel(name = "健康证")
+    @ApiModelProperty("健康证")
+    private String healthCertificate;
+
+    /**
+     * 从业资格证
+     */
+    @Excel(name = "从业资格证")
+    @ApiModelProperty("从业资格证")
+    private String qualificationCertificate;
+
+    /**
+     * 无犯罪证明
+     */
+    @Excel(name = "无犯罪证明")
+    @ApiModelProperty("无犯罪证明")
+    private String noCrimeRecord;
+
+    /**
+     * 承诺书
+     */
+    @Excel(name = "承诺书")
+    @ApiModelProperty("承诺书")
+    private String commitmentPdf;
+    /**
+     * 承诺录音
+     */
+    @Excel(name = "承诺录音")
+    @ApiModelProperty("承诺录音")
+    private String commitmentAudio;
+    /**
+     * 承诺录像
+     */
+    @Excel(name = "承诺录像")
+    @ApiModelProperty("承诺录像")
+    private String commitmentVideo;
+    /**
+     * 审核状态:0-待审核,1-待审核,2-审核通过,3-审核驳回
+     */
+    @Excel(name = "审核状态:0-待入驻,1-待审核,2-审核通过,3-审核驳回")
+    @ApiModelProperty("审核状态:0-待入驻,1-待审核,2-审核通过,3-审核驳回")
+    private int auditStatus;
+    /**
+     * 审批时间
+     */
+    @Excel(name = "审批时间")
+    @ApiModelProperty("审批时间")
+    private Date approveTime;
     @ApiModelProperty("项目id集合")
     private ArrayList<Long> projectIds;
 

+ 89 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianAuditListVO.java

@@ -0,0 +1,89 @@
+package com.ylx.massage.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 后台商户入驻审核列表展示对象。
+ */
+@Data
+@ApiModel(value = "MaTechnicianAuditListVO", description = "后台商户入驻审核列表展示对象")
+public class MaTechnicianAuditListVO {
+
+    /**
+     * 商户ID。
+     */
+    @ApiModelProperty("商户ID")
+    private Long merchantId;
+
+    /**
+     * 姓名。
+     */
+    @ApiModelProperty("姓名")
+    private String teName;
+
+    /**
+     * 性别编码:0-女,1-男。
+     */
+    @ApiModelProperty("性别:0-女 1-男")
+    private Integer teSex;
+
+    /**
+     * 性别展示名称。
+     */
+    @ApiModelProperty("性别名称")
+    private String teSexName;
+
+    /**
+     * 电话。
+     */
+    @ApiModelProperty("电话")
+    private String tePhone;
+
+    /**
+     * 形象照。
+     */
+    @ApiModelProperty("形象照")
+    private String avatar;
+
+    /**
+     * 意向城市。
+     */
+    @ApiModelProperty("意向城市")
+    private String teAddress;
+
+    /**
+     * 开通服务名称。
+     */
+    @ApiModelProperty("开通服务")
+    private String openServiceName;
+
+    /**
+     * 申请时间。
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty("申请时间")
+    private Date applyTime;
+
+    /**
+     * 审核状态编码:0-待入驻,1-待审核,2-审核通过,3-审核驳回。
+     */
+    @ApiModelProperty("审核状态:0-待入驻 1-待审核 2-审核通过 3-审核驳回")
+    private Integer auditStatus;
+
+    /**
+     * 审核状态名称。
+     */
+    @ApiModelProperty("审核状态名称")
+    private String auditStatusName;
+
+    /**
+     * 用户类型:0-正式用户,1-虚拟用户。
+     */
+    @ApiModelProperty("用户类型:0-正式用户 1-虚拟用户")
+    private Integer techType;
+}

+ 97 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianMerchantDetailVO.java

@@ -0,0 +1,97 @@
+package com.ylx.massage.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 后台商户详情展示对象
+ */
+@ApiModel(value = "MaTechnicianMerchantDetailVO", description = "后台商户详情展示对象")
+@Data
+public class MaTechnicianMerchantDetailVO {
+
+    /**
+     * 商户唯一标识。
+     */
+    @ApiModelProperty("商户ID")
+    private Long merchantId;
+
+    /**
+     * 商户真实姓名。
+     */
+    @ApiModelProperty("姓名")
+    private String teName;
+
+    /**
+     * 商户在平台对外展示的昵称。
+     */
+    @ApiModelProperty("昵称")
+    private String teNickName;
+
+    /**
+     * 商户性别编码:0-女,1-男。
+     */
+    @ApiModelProperty("性别:0-女 1-男")
+    private Integer teSex;
+
+    /**
+     * 商户性别展示名称。
+     */
+    @ApiModelProperty("性别名称")
+    private String teSexName;
+
+    /**
+     * 商户联系电话。
+     */
+    @ApiModelProperty("电话")
+    private String tePhone;
+
+    /**
+     * 商户开通的服务类目ID,多个用英文逗号分隔。
+     */
+    @ApiModelProperty("服务类目ID集合,逗号分隔")
+    private String openService;
+
+    /**
+     * 商户开通的服务类目名称
+     */
+    @ApiModelProperty("商户开通的服务类目名称")
+    private String serviceCategoryName;
+
+    /**
+     * 商户开通的服务项目ID,多个用英文逗号分隔。
+     */
+    @ApiModelProperty("服务项目ID集合,逗号分隔")
+    private String projectIds;
+
+    /**
+     * 商户开通的服务项目名称,多个用斜杠分隔。
+     */
+    @ApiModelProperty("服务项目名称")
+    private String serviceProjectName;
+
+    /**
+     * 商户类型编码:0-正式商户,1-虚拟商户。
+     */
+    @ApiModelProperty("商户类型:0-正式商户 1-虚拟商户")
+    private Integer techType;
+
+    /**
+     * 商户类型展示名称。
+     */
+    @ApiModelProperty("商户类型名称")
+    private String techTypeName;
+
+    /**
+     * 首页是否推荐:0-否,1-是。
+     */
+    @ApiModelProperty("是否推荐:0-否 1-是")
+    private Integer isRecommend;
+
+    /**
+     * 首页是否推荐展示名称。
+     */
+    @ApiModelProperty("是否推荐名称")
+    private String isRecommendName;
+}

+ 138 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianMerchantListVO.java

@@ -0,0 +1,138 @@
+package com.ylx.massage.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 后台商户列表展示对象
+ */
+@ApiModel(value = "MaTechnicianMerchantListVO", description = "后台商户列表展示对象")
+@Data
+public class MaTechnicianMerchantListVO {
+
+    /**
+     * 商户唯一标识。
+     */
+    @ApiModelProperty("商户ID")
+    private Long merchantId;
+
+    /**
+     * 商户真实姓名。
+     */
+    @ApiModelProperty("姓名")
+    private String teName;
+
+    /**
+     * 商户在平台对外展示的昵称。
+     */
+    @ApiModelProperty("昵称")
+    private String teNickName;
+
+    /**
+     * 商户性别编码:0-女,1-男。
+     */
+    @ApiModelProperty("性别:0-女 1-男")
+    private Integer teSex;
+
+    /**
+     * 商户性别展示名称。
+     */
+    @ApiModelProperty("性别名称")
+    private String teSexName;
+
+    /**
+     * 商户头像地址。
+     */
+    @ApiModelProperty("头像")
+    private String teAvatar;
+
+    /**
+     * 商户联系电话。
+     */
+    @ApiModelProperty("电话")
+    private String tePhone;
+
+    /**
+     * 平台评分。
+     */
+    @ApiModelProperty("评分")
+    private BigDecimal nStar;
+
+    /**
+     * 服务状态编码:0-服务中,1-待接单,2-休息中。
+     */
+    @ApiModelProperty("服务状态:0-服务中 1-待接单 2-休息中")
+    private Integer serviceState;
+
+    /**
+     * 服务状态展示名称。
+     */
+    @ApiModelProperty("服务状态名称")
+    private String serviceStateName;
+
+    /**
+     * 累计已服务订单数量。
+     */
+    @ApiModelProperty("已服务数量")
+    private Integer nNum;
+
+    /**
+     * 累计在线时长,单位:分钟。
+     */
+    @ApiModelProperty("累计在线时长,单位:分钟")
+    private Integer onlineTime;
+
+    /**
+     * 商户开通的服务类目或服务项目名称。
+     */
+    @ApiModelProperty("服务类目")
+    private String teProject;
+
+    /**
+     * 上架状态编码:0-下架,1-上架。
+     */
+    @ApiModelProperty("上架状态:0-下架 1-上架")
+    private Integer postState;
+
+    /**
+     * 首页是否推荐:0-否,1-是。
+     */
+    @ApiModelProperty("是否推荐:0-否 1-是")
+    private Integer isRecommend;
+
+    /**
+     * 用户类型编码:0-正式用户,1-虚拟用户。
+     */
+    @ApiModelProperty("用户类型:0-正式用户 1-虚拟用户")
+    private Integer techType;
+
+    /**
+     * 用户类型展示名称。
+     */
+    @ApiModelProperty("用户类型名称")
+    private String techTypeName;
+
+    /**
+     * 商户管理状态编码:0-正常,1-限制接单,2-冻结,3-注销。
+     */
+    @ApiModelProperty("管理状态:0-正常 1-限制接单 2-冻结 3-注销")
+    private String merchantStatus;
+
+    /**
+     * 商户管理状态展示名称。
+     */
+    @ApiModelProperty("管理状态名称")
+    private String merchantStatusName;
+
+    /**
+     * 商户创建时间。
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty("创建时间")
+    private Date createTime;
+}

+ 55 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MassageAllMerchantsVo.java

@@ -0,0 +1,55 @@
+package com.ylx.massage.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 类描述:首页按摩商户推荐点击全部返回所有按摩商户vo
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/4 9:14
+ */
+@Data
+public class MassageAllMerchantsVo {
+    /**
+     * 项目ID
+     */
+    @ApiModelProperty("项目ID")
+    private String projectId;
+    /**
+     * 项目名称
+     */
+    @ApiModelProperty("项目名称")
+    private String projectName;
+    /**
+     * 商户名称
+     */
+    @ApiModelProperty("商户名称")
+    private String merchantName;
+
+    /**
+     * 商户id
+     */
+    @ApiModelProperty("商户id")
+    private Long merchantId;
+
+    /**
+     * 已服务数量
+     */
+    @ApiModelProperty("已服务订单数量")
+    private Integer nNum;
+    /**
+     * 技师距离(公里)
+     */
+    @ApiModelProperty("技师距离(公里)")
+    private BigDecimal distanceShow;
+    /**
+     * 头像
+     */
+    @ApiModelProperty("商户头像")
+    private String teAvatar;
+
+}

+ 39 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MassageProjectRecommendVo.java

@@ -0,0 +1,39 @@
+package com.ylx.massage.domain.vo;
+
+import com.ylx.common.annotation.Excel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 类描述:首页按摩推荐项目
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/3 15:43
+ */
+@Data
+public class MassageProjectRecommendVo {
+    @ApiModelProperty("项目id")
+    private Long projectId;
+
+
+    /** 项目名称 */
+    @ApiModelProperty("项目名称")
+    private String projectName;
+
+    /** 项目时长(分) */
+    @ApiModelProperty("项目时长(分)")
+    private Long projectDuration;
+
+    @ApiModelProperty("销量")
+    private Integer sales;
+
+    @ApiModelProperty("均价")
+    private BigDecimal avgCurrentPrice;
+
+    @ApiModelProperty("亮点")
+    private String highlight;
+
+}

+ 25 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MassageRecommendVo.java

@@ -0,0 +1,25 @@
+package com.ylx.massage.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 类描述:首页按摩推荐vo
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/2 15:38
+ */
+@Data
+public class MassageRecommendVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 推荐商户列表:评分前五
+     */
+    private List<MerchantVo> merchantVoList;
+
+    //todo 推荐项目列表
+}

+ 59 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MerchantVo.java

@@ -0,0 +1,59 @@
+package com.ylx.massage.domain.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ylx.common.annotation.Excel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 类描述:商户vo
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/2 15:41
+ */
+@Data
+public class MerchantVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @ApiModelProperty("商户id")
+    private Long merchantId;
+
+    /**
+     * 姓名
+     */
+    @ApiModelProperty("商户名")
+    private String teName;
+    /**
+     * 评分
+     */
+    @ApiModelProperty("评分")
+    private Integer nStar;
+    /**
+     * 已服务数量
+     */
+    @ApiModelProperty("已服务订单数量")
+    private Integer nNum;
+
+    @ApiModelProperty("技师距离(公里)")
+    private BigDecimal distanceShow;
+
+    /**
+     * 商户开通技能时设置的最低价格
+     */
+    @ApiModelProperty("最低价格")
+    private BigDecimal price;
+
+    /**
+     * 商户形象照
+     */
+    @ApiModelProperty("形象照,展示")
+    private String avatar;
+
+}

+ 198 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/Result.java

@@ -0,0 +1,198 @@
+package com.ylx.massage.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.ylx.massage.constant.CommonConstant;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *   接口返回数据格式
+ * @author scott
+ * @email jeecgos@163.com
+ * @date  2019年1月19日
+ */
+@Data
+@ApiModel(value="接口返回对象", description="接口返回对象")
+public class Result<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 成功标志
+     */
+    @ApiModelProperty(value = "成功标志")
+    private boolean success = true;
+
+    /**
+     * 返回处理消息
+     */
+    @ApiModelProperty(value = "返回处理消息")
+    private String message = "";
+
+    /**
+     * 返回代码
+     */
+    @ApiModelProperty(value = "返回代码")
+    private Integer code = 0;
+
+    /**
+     * 返回数据对象 data
+     */
+    @ApiModelProperty(value = "返回数据对象")
+    private T result;
+
+    /**
+     * 时间戳
+     */
+    @ApiModelProperty(value = "时间戳")
+    private long timestamp = System.currentTimeMillis();
+
+    public Result() {
+    }
+
+    /**
+     * 兼容VUE3版token失效不跳转登录页面
+     * @param code
+     * @param message
+     */
+    public Result(Integer code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public Result<T> success(String message) {
+        this.message = message;
+        this.code = CommonConstant.SC_OK_200;
+        this.success = true;
+        return this;
+    }
+
+    public static<T> Result<T> ok() {
+        Result<T> r = new Result<T>();
+        r.setSuccess(true);
+        r.setCode(CommonConstant.SC_OK_200);
+        return r;
+    }
+
+    public static<T> Result<T> ok(String msg) {
+        Result<T> r = new Result<T>();
+        r.setSuccess(true);
+        r.setCode(CommonConstant.SC_OK_200);
+        //Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
+        r.setResult((T) msg);
+        r.setMessage(msg);
+        return r;
+    }
+
+    public static<T> Result<T> ok(T data) {
+        Result<T> r = new Result<T>();
+        r.setSuccess(true);
+        r.setCode(CommonConstant.SC_OK_200);
+        r.setResult(data);
+        return r;
+    }
+
+    public static<T> Result<T> OK() {
+        Result<T> r = new Result<T>();
+        r.setSuccess(true);
+        r.setCode(CommonConstant.SC_OK_200);
+        return r;
+    }
+
+    /**
+     * 此方法是为了兼容升级所创建
+     *
+     * @param msg
+     * @param <T>
+     * @return
+     */
+    public static<T> Result<T> OK(String msg) {
+        Result<T> r = new Result<T>();
+        r.setSuccess(true);
+        r.setCode(CommonConstant.SC_OK_200);
+        r.setMessage(msg);
+        //Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
+        r.setResult((T) msg);
+        return r;
+    }
+
+    public static<T> Result<T> OK(String msg,boolean success) {
+        Result<T> r = new Result<T>();
+        r.setSuccess(success);
+        r.setCode(CommonConstant.SC_OK_200);
+        r.setMessage(msg);
+        r.setResult((T) msg);
+        return r;
+    }
+
+    public static<T> Result<T> OK(T data) {
+        Result<T> r = new Result<T>();
+        r.setSuccess(true);
+        r.setCode(CommonConstant.SC_OK_200);
+        r.setResult(data);
+        return r;
+    }
+
+    public static<T> Result<T> OK(String msg, T data) {
+        Result<T> r = new Result<T>();
+        r.setSuccess(true);
+        r.setCode(CommonConstant.SC_OK_200);
+        r.setMessage(msg);
+        r.setResult(data);
+        return r;
+    }
+
+    public static <T> Result<T> ok(String msg, T data) {
+        Result<T> r = new Result<>();
+        r.setMessage(msg) ;
+        r.setResult(data);
+        return r;
+    }
+
+    public static<T> Result<T> error(String msg, T data) {
+        Result<T> r = new Result<T>();
+        r.setSuccess(false);
+        r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
+        r.setMessage(msg);
+        r.setResult(data);
+        return r;
+    }
+
+    public static<T> Result<T> error(String msg) {
+        return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
+    }
+
+    public static<T> Result<T> error(int code, String msg) {
+        Result<T> r = new Result<T>();
+        r.setCode(code);
+        r.setMessage(msg);
+        r.setSuccess(false);
+        return r;
+    }
+
+    public Result<T> errorNew(int code, String msg) {
+        this.message = msg;
+        this.code = code;
+        this.success = false;
+        return this;
+    }
+
+    public Result<T> error500(String message) {
+        this.message = message;
+        this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
+        this.success = false;
+        return this;
+    }
+
+    /**
+     * 无权限访问返回结果
+     */
+    public static<T> Result<T> noauth(String msg) {
+        return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
+    }
+
+    @JsonIgnore
+    private String onlTable;
+}

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

@@ -0,0 +1,22 @@
+package com.ylx.massage.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+@Data
+public class ThirdPartyLoginsVo implements Serializable {
+    @ApiModelProperty(value = "用户手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "用户类型 1-用户 2-商户")
+    private String userType;
+    @ApiModelProperty(value = "是否开启验证码登录 true-是 false-否")
+    private Boolean codeSwitch;
+    @ApiModelProperty(value = "验证码")
+    private String phoneMsg;
+
+    @ApiModelProperty(value = "密码")
+    private String passWord;
+
+}

+ 16 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/CityOperationApplicationMapper.java

@@ -0,0 +1,16 @@
+package com.ylx.massage.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.massage.domain.CityOperationApplication;
+
+
+/**
+ * 城市管理表(CityOperationApplication)表数据库访问层
+ *
+ * @author makejava
+ * @since 2026-06-04 15:53:41
+ */
+public interface CityOperationApplicationMapper extends BaseMapper<CityOperationApplication> {
+
+}
+

+ 8 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/ContractRecordMapper.java

@@ -0,0 +1,8 @@
+package com.ylx.massage.mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.massage.domain.ContractRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface ContractRecordMapper extends BaseMapper<ContractRecord> {
+}

+ 23 - 2
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaProjectMapper.java

@@ -1,7 +1,16 @@
 package com.ylx.massage.mapper;
 
 import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ylx.massage.domain.MaProject;
+import com.ylx.massage.domain.dto.MassageAllMerchantsDto;
+import com.ylx.massage.domain.vo.MassageAllMerchantsVo;
+import com.ylx.massage.domain.vo.MassageProjectRecommendVo;
+import com.ylx.project.domain.bookMerchant.vo.BookMerchantVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 服务项目Mapper接口
@@ -9,8 +18,8 @@ import com.ylx.massage.domain.MaProject;
  * @author ylx
  * @date 2024-03-20
  */
-public interface MaProjectMapper
-{
+@Mapper
+public interface MaProjectMapper extends BaseMapper<MaProject> {
     /**
      * 查询服务项目
      *
@@ -58,4 +67,16 @@ public interface MaProjectMapper
      * @return 结果
      */
     public int deleteMaProjectByIds(String[] ids);
+
+    /**
+     * 首页项目推荐
+     *
+     * @param page
+     * @return
+     */
+    Page<MassageProjectRecommendVo> getMassageProjectRecommend(@Param("page") Page<MassageProjectRecommendVo> page, @Param("cityCode") String cityCode);
+
+    Page<MassageAllMerchantsVo> getMassageAllMerchants(@Param("page") Page<MassageAllMerchantsVo> page, @Param("dto") MassageAllMerchantsDto dto);
+
+    Page<BookMerchantVO> selectMerchantList(@Param("page") Page<BookMerchantVO> page, @Param("projectId") Long projectId);
 }

+ 34 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTeProjectMapper.java

@@ -0,0 +1,34 @@
+package com.ylx.massage.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.massage.domain.MaTeProject;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 商户服务项目关联Mapper接口
+ */
+@Mapper
+public interface MaTeProjectMapper extends BaseMapper<MaTeProject> {
+
+    /**
+     * 批量新增商户服务项目关联
+     *
+     * @param entities 关联集合
+     * @return 影响行数
+     */
+    int insertBatch(@Param("entities") List<MaTeProject> entities);
+
+    /**
+     * 根据商户ID删除服务项目关联。
+     *
+     * @param technicianId 商户ID
+     * @return 影响行数
+     */
+    default int deleteByTechnicianId(Long technicianId) {
+        return delete(new LambdaQueryWrapper<MaTeProject>().eq(MaTeProject::getTeId, technicianId));
+    }
+}

+ 97 - 2
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTechnicianMapper.java

@@ -1,7 +1,21 @@
 package com.ylx.massage.mapper;
 
 import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ylx.massage.domain.MaTechnician;
+import com.ylx.massage.domain.dto.MaTechnicianAuditQueryDTO;
+import com.ylx.massage.domain.dto.MaTechnicianMerchantQueryDTO;
+import com.ylx.massage.domain.dto.MassageMerchantRecommendDto;
+import com.ylx.massage.domain.vo.MaTechnicianAuditListVO;
+import com.ylx.massage.domain.vo.MaTechnicianMerchantDetailVO;
+import com.ylx.massage.domain.vo.MaTechnicianMerchantListVO;
+import com.ylx.massage.domain.vo.MassageProjectRecommendVo;
+import com.ylx.massage.domain.vo.MerchantVo;
+import org.apache.ibatis.annotations.Param;
+import org.mapstruct.Mapper;
+import org.apache.ibatis.annotations.Select;
 
 /**
  * 技师Mapper接口
@@ -9,7 +23,8 @@ import com.ylx.massage.domain.MaTechnician;
  * @author ylx
  * @date 2024-03-22
  */
-public interface MaTechnicianMapper
+@Mapper
+public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
 {
     /**
      * 查询技师
@@ -25,7 +40,7 @@ public interface MaTechnicianMapper
      * @param maTechnician 技师
      * @return 技师集合
      */
-    public List<MaTechnician> selectMaTechnicianList(MaTechnician maTechnician);
+     List<MaTechnician> selectMaTechnicianList(MaTechnician maTechnician);
 
     /**
      * 新增技师
@@ -58,4 +73,84 @@ public interface MaTechnicianMapper
      * @return 结果
      */
     public int deleteMaTechnicianByIds(Long[] ids);
+    /**
+     * 后台查询商户列表
+     *
+     * @param page 分页参数
+     * @param dto 查询条件
+     * @return 商户分页列表
+     */
+    Page<MaTechnicianMerchantListVO> selectMerchantList(Page<MaTechnicianMerchantListVO> page,
+                                                        @Param("dto") MaTechnicianMerchantQueryDTO dto);
+
+    /**
+     * 后台查询商户入驻审核列表
+     *
+     * @param page 分页参数
+     * @param dto 查询条件
+     * @return 商户入驻审核分页列表
+     */
+    Page<MaTechnicianAuditListVO> selectMerchantAuditList(Page<MaTechnicianAuditListVO> page,
+                                                          @Param("dto") MaTechnicianAuditQueryDTO dto);
+
+    /**
+     * 后台查询商户详情
+     *
+     * @param id 商户ID
+     * @return 商户详情
+     */
+    MaTechnicianMerchantDetailVO selectMerchantDetailById(@Param("id") Long id);
+
+    /**
+     * 根据商户ID查询商户。
+     *
+     * @param id 商户ID
+     * @return 商户信息
+     */
+    default MaTechnician selectMerchantById(Integer id) {
+        return selectById(id);
+    }
+
+    /**
+     * 根据商户ID更新商户。
+     *
+     * @param maTechnician 商户信息
+     * @return 影响行数
+     */
+    default int updateMerchantById(MaTechnician maTechnician) {
+        return updateById(maTechnician);
+    }
+
+    /**
+     * 根据商户ID更新合同文件。
+     *
+     * @param maTechnician 商户合同文件信息
+     * @return 影响行数
+     */
+    default int updateMerchantContractById(MaTechnician maTechnician) {
+        return updateById(maTechnician);
+    }
+
+    List<MerchantVo> getMerchantRecommend(@Param("dto") MassageMerchantRecommendDto dto);
+
+    /**
+     * 查询该城市是否有商户提供服务
+     * @param areaCode
+     * @return
+     */
+    @Select("SELECT EXISTS (" +
+            "    SELECT 1" +
+            "    FROM ma_technician t" +
+            "    INNER JOIN ma_project p ON t.id = p.merchant_id" +
+            "    WHERE t.te_area_code = #{areaCode}" +
+            "      AND t.is_delete = 0" +
+            "      AND t.audit_status = 2" +
+            "      AND t.n_status2 = 0" +
+            "      AND t.merchant_status = 0" +
+            "      AND p.is_delete = 0" +
+            "      AND p.audit_status = 1" +
+            "      AND p.merchant_type = 0" +
+            "      AND p.project_is_enable = 1" +
+            ") AS has_merchant_with_service")
+    Boolean isHasMerchantCity(@Param("areaCode")  String areaCode);
 }

+ 5 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/AreaService.java

@@ -5,6 +5,7 @@ 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.userhome.area.domain.vo.CityVo;
 
 import java.util.List;
 
@@ -19,5 +20,9 @@ public interface AreaService extends IService<Area> {
     List<AreaTreeNode> getAreaTree();
 
     CityInfoVo getCityInfoByCoordinates(CoordinateDTO dto);
+
+    List<CityVo> getHomeList ();
+
+    List<CityVo> getCityName(String name);
 }
 

+ 18 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/CityOperationApplicationService.java

@@ -0,0 +1,18 @@
+package com.ylx.massage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ylx.massage.domain.CityOperationApplication;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * 城市管理表(CityOperationApplication)表服务接口
+ *
+ * @author makejava
+ * @since 2026-06-04 14:23:52
+ */
+@Service("cityOperationApplicationService")
+public interface CityOperationApplicationService extends IService<CityOperationApplication> {
+
+}
+

Some files were not shown because too many files changed in this diff