Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/dev' into dev

jinwenhai 1 týždeň pred
rodič
commit
877ea56bdd
52 zmenil súbory, kde vykonal 2250 pridanie a 24 odobranie
  1. 4 1
      nightFragrance-massage/pom.xml
  2. 115 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/controller/AttendanceConfigController.java
  3. 110 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/AttendanceDeductionRule.java
  4. 103 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/AttendanceRule.java
  5. 38 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/dto/AttendanceDeductionRuleDTO.java
  6. 49 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/dto/AttendanceRuleAddDTO.java
  7. 32 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/vo/AttendanceDeductionRuleVO.java
  8. 46 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/domain/vo/AttendanceRuleDetailVO.java
  9. 7 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/mapper/AttendanceDeductionRuleMapper.java
  10. 7 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/mapper/AttendanceRuleMapper.java
  11. 17 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/service/AttendanceConfigService.java
  12. 220 0
      nightFragrance-massage/src/main/java/com/ylx/attendanceconfig/service/impl/AttendanceConfigServiceImpl.java
  13. 177 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/controller/GiftCardManageController.java
  14. 20 16
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/GiftCard.java
  15. 29 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageQueryDTO.java
  16. 93 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageSaveDTO.java
  17. 19 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardManageUpdateDTO.java
  18. 25 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/dto/GiftCardPublishStatusDTO.java
  19. 16 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManageDetailVO.java
  20. 42 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManageExportVO.java
  21. 54 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/vo/GiftCardManagePageVO.java
  22. 8 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/mapper/GiftCardMapper.java
  23. 22 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/service/IGiftCardService.java
  24. 3 1
      nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardOrderServiceImpl.java
  25. 255 0
      nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardServiceImpl.java
  26. 39 0
      nightFragrance-massage/src/main/java/com/ylx/massage/controller/MassageRecommendController.java
  27. 23 1
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTechnician.java
  28. 6 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TAddress.java
  29. 31 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MassageRecommendDto.java
  30. 25 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MassageRecommendVo.java
  31. 53 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MerchantVo.java
  32. 8 1
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTechnicianMapper.java
  33. 5 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/AreaService.java
  34. 12 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/IMaTechnicianService.java
  35. 10 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/IMassageRecommendService.java
  36. 79 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/AreaServiceImpl.java
  37. 27 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/MaTechnicianServiceImpl.java
  38. 30 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/MassageRecommendServiceImpl.java
  39. 46 0
      nightFragrance-massage/src/main/java/com/ylx/order/domain/OrderStatusFlow.java
  40. 7 0
      nightFragrance-massage/src/main/java/com/ylx/order/mapper/OrderStatusFlowMapper.java
  41. 15 0
      nightFragrance-massage/src/main/java/com/ylx/order/service/OrderStatusFlowService.java
  42. 25 0
      nightFragrance-massage/src/main/java/com/ylx/order/service/OrderStatusFlowServiceImpl.java
  43. 3 2
      nightFragrance-massage/src/main/java/com/ylx/shopingfundsdetail/controller/ShoppingFundsDetailController.java
  44. 2 1
      nightFragrance-massage/src/main/java/com/ylx/shoppingfunds/controller/MyShoppingFundsController.java
  45. 28 0
      nightFragrance-massage/src/main/java/com/ylx/userhome/area/constants/CityCodeTypeEnum.java
  46. 73 0
      nightFragrance-massage/src/main/java/com/ylx/userhome/area/controller/CustomerCityController.java
  47. 49 0
      nightFragrance-massage/src/main/java/com/ylx/userhome/area/domain/vo/CityVo.java
  48. 76 0
      nightFragrance-massage/src/main/java/com/ylx/userhome/area/util/CityChineseUtil.java
  49. 21 1
      nightFragrance-massage/src/main/resources/mapper/giftCard/GiftCardMapper.xml
  50. 31 0
      nightFragrance-massage/src/main/resources/mapper/massage/MaTechnicianMapper.xml
  51. 9 0
      nightFragrance-massage/src/main/resources/mapper/order/OrderStatusFlowMapper.xml
  52. 6 0
      pom.xml

+ 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());
+    }
+}

+ 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("删除购物卡失败");
+        }
+    }
+}

+ 20 - 16
nightFragrance-massage/src/main/java/com/ylx/giftCard/domain/GiftCard.java

@@ -2,9 +2,12 @@ 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;
@@ -14,6 +17,7 @@ import java.time.LocalDate;
  */
 @EqualsAndHashCode(callSuper = true)
 @Data
+@TableName("gift_card")
 public class GiftCard extends BaseEntity {
     private static final long serialVersionUID = 2378338959829222898L;
 
@@ -33,11 +37,26 @@ public class GiftCard extends BaseEntity {
      */
     private String name;
 
+    /**
+     * 缩略图URL(上传后存储路径)
+     */
+    private String imageUrl;
+
     /**
      * 面值金额,单位:元
      */
     private BigDecimal amount;
 
+    /**
+     * 商户提成比例,如 1.00 表示 1%
+     */
+    private BigDecimal commissionRate;
+
+    /**
+     * 是否上架:1=上架,0=下架
+     */
+    private Integer isPublished;
+
     /**
      * 库存数量,0表示无库存
      */
@@ -58,21 +77,6 @@ public class GiftCard extends BaseEntity {
      */
     private LocalDate validEndDate;
 
-    /**
-     * 商户提成比例,如 1.00 表示 1%
-     */
-    private BigDecimal commissionRate;
-
-    /**
-     * 缩略图URL(上传后存储路径)
-     */
-    private String imageUrl;
-
-    /**
-     * 是否上架:1=上架,0=下架
-     */
-    private Integer isPublished;
-
     /**
      * 详情(富文本,可存HTML或Markdown)
      */
@@ -81,6 +85,6 @@ public class GiftCard extends BaseEntity {
     /**
      * 是否删除:0=否,1=是。
      */
+    @TableLogic
     private Integer isDelete;
-
 }

+ 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;
+}

+ 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;
+}

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

@@ -2,7 +2,15 @@ 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);
 }

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

@@ -2,10 +2,18 @@ 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 {
@@ -15,4 +23,18 @@ public interface IGiftCardService {
     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);
 }

+ 3 - 1
nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardOrderServiceImpl.java

@@ -37,6 +37,8 @@ public class GiftCardOrderServiceImpl extends ServiceImpl<GiftCardOrderMapper, G
     private TWxUserService wxUserService;
     @Resource
     private ShoppingFundsDetailService shoppingFundsDetailService;
+    // 充值
+    public static final int EXPENSE_TYPE_RECHARGE = 0;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -131,7 +133,7 @@ public class GiftCardOrderServiceImpl extends ServiceImpl<GiftCardOrderMapper, G
         dto.setUserId(wxUser.getId());
         dto.setAmount(payAmount);
         dto.setOrderNo(result.getOutTradeNo());
-        dto.setExpenseType(0); // 充值
+        dto.setExpenseType(EXPENSE_TYPE_RECHARGE);
         dto.setBalance(newBalance);
         dto.setGiftCardId(cardOrder.getGiftCardId());
         shoppingFundsDetailService.addShoppingFundsDetail(dto);

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

@@ -3,19 +3,28 @@ 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;
@@ -25,6 +34,10 @@ 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;
@@ -40,6 +53,8 @@ public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> i
 
     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) {
@@ -125,6 +140,112 @@ public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> i
         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("删除购物卡失败");
+        }
+    }
+
     /**
      * 校验购物卡有效性
      */
@@ -195,4 +316,138 @@ public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> i
         }
     }
 
+    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());
+    }
+
 }

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

@@ -0,0 +1,39 @@
+package com.ylx.massage.controller;
+
+import com.ylx.common.core.domain.R;
+import com.ylx.massage.domain.dto.MassageRecommendDto;
+import com.ylx.massage.domain.vo.MassageRecommendVo;
+import com.ylx.massage.domain.vo.MerchantVo;
+import com.ylx.massage.service.IMaTechnicianService;
+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.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 = "/getMassageRecommend")
+    public R<List<MassageRecommendVo>> getMassageRecommend(@RequestBody MassageRecommendDto dto) {
+        List<MassageRecommendVo> massageRecommendVos = massageRecommendService.getMassageRecommend(dto);
+        return R.ok(massageRecommendVos);
+    }
+}

+ 23 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTechnician.java

@@ -10,9 +10,9 @@ import lombok.experimental.Accessors;
 import com.ylx.common.annotation.Excel;
 import com.ylx.common.core.domain.BaseEntity;
 
-import java.math.BigInteger;
 import java.util.Date;
 
+
 /**
  * 技师对象 ma_technician
  *
@@ -70,6 +70,14 @@ public class MaTechnician extends BaseEntity
     @TableField("te_address")
     private String teAddress;
 
+    /**
+     * 城市编码
+     */
+    private String teAreaCode;
+
+    /**
+     * 年龄
+     */
     /** 年龄 */
     @Excel(name = "年龄")
     @TableField("te_age")
@@ -170,6 +178,20 @@ public class MaTechnician extends BaseEntity
     @Excel(name = "是否删除(0否1是)")
     private Long isDelete;
 
+    /**
+     * 评分
+     * 用户对技师的评分,通常为1-5分
+     */
+    @ApiModelProperty("评分")
+    private Integer nStar;
+
+    /**
+     * 已服务数量
+     * 技师累计完成的订单总数
+     */
+    @TableField("n_num")
+    @ApiModelProperty("已服务订单数量")
+    private Integer nNum;
 
 
 }

+ 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
      */

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

@@ -0,0 +1,31 @@
+package com.ylx.massage.domain.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 类描述:按摩推荐dto
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/2 16:41
+ */
+@Data
+public class MassageRecommendDto {
+    /**
+     * 经度
+     * 用户当前位置的经度坐标
+     */
+    @ApiModelProperty("用户经度")
+    private BigDecimal longitude;
+
+    /**
+     * 纬度
+     * 用户当前位置的纬度坐标
+     */
+    @ApiModelProperty("用户纬度")
+    private BigDecimal latitude;
+}

+ 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 推荐项目列表
+}

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

@@ -0,0 +1,53 @@
+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 distance ;
+
+    /**
+     * 商户开通技能时设置的最低价格
+     */
+    @ApiModelProperty("最低价格")
+    private BigDecimal price;
+
+}

+ 8 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTechnicianMapper.java

@@ -1,7 +1,12 @@
 package com.ylx.massage.mapper;
 
 import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.ylx.massage.domain.MaTechnician;
+import com.ylx.massage.domain.dto.MassageRecommendDto;
+import com.ylx.massage.domain.vo.MerchantVo;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 技师Mapper接口
@@ -9,7 +14,7 @@ import com.ylx.massage.domain.MaTechnician;
  * @author ylx
  * @date 2024-03-22
  */
-public interface MaTechnicianMapper
+public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
 {
     /**
      * 查询技师
@@ -58,4 +63,6 @@ public interface MaTechnicianMapper
      * @return 结果
      */
     public int deleteMaTechnicianByIds(Long[] ids);
+
+    List<MerchantVo> getMerchantRecommend(@Param("dto") MassageRecommendDto dto);
 }

+ 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);
 }
 

+ 12 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/IMaTechnicianService.java

@@ -2,7 +2,10 @@ package com.ylx.massage.service;
 
 import java.util.List;
 import com.ylx.massage.domain.MaTechnician;
+import com.ylx.massage.domain.dto.MassageRecommendDto;
 import com.ylx.massage.domain.vo.MaTechnicianAppAddVo;
+import com.ylx.massage.domain.vo.MassageRecommendVo;
+import com.ylx.massage.domain.vo.MerchantVo;
 
 /**
  * 技师Service接口
@@ -59,4 +62,13 @@ public interface IMaTechnicianService
      * @return 结果
      */
     public int deleteMaTechnicianById(Long id);
+
+    /**
+     * 首页选中的城市是否有开通服务
+     * @param areaCode
+     * @return
+     */
+    Boolean isHasMerchantCity(String areaCode);
+
+    List<MerchantVo> getMerchantRecommend(MassageRecommendDto dto);
 }

+ 10 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/IMassageRecommendService.java

@@ -0,0 +1,10 @@
+package com.ylx.massage.service;
+
+import com.ylx.massage.domain.dto.MassageRecommendDto;
+import com.ylx.massage.domain.vo.MassageRecommendVo;
+
+import java.util.List;
+
+public interface IMassageRecommendService {
+    List<MassageRecommendVo> getMassageRecommend(MassageRecommendDto dto);
+}

+ 79 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/AreaServiceImpl.java

@@ -16,10 +16,16 @@ import com.ylx.massage.domain.dto.CoordinateDTO;
 import com.ylx.massage.domain.vo.*;
 import com.ylx.massage.mapper.AreaMapper;
 import com.ylx.massage.service.AreaService;
+import com.ylx.userhome.area.constants.CityCodeTypeEnum;
+import com.ylx.userhome.area.domain.vo.CityVo;
+import com.ylx.userhome.area.util.CityChineseUtil;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -38,6 +44,10 @@ public class AreaServiceImpl extends ServiceImpl<AreaMapper, Area> implements Ar
     private static final String ERROR_MSG_CITY_NOT_FOUND = "城市数据不存在";
     private static final int MAX_QUERY_RESULTS = 1;
 
+    private static final List<String> PROVINCE_CODE_LIST = Arrays.asList("110100", "120100", "310100", "500100");
+
+    private final static String CITY_TYPE = "市辖区";
+
     /**
      * 一次性查询所有区域,内存构建树形结构
      */
@@ -155,4 +165,73 @@ public class AreaServiceImpl extends ServiceImpl<AreaMapper, Area> implements Ar
 
         return new CityInfoVo(area.getName(), area.getCode());
     }
+
+    /**
+     * 用户首页地区查询
+     * @return
+     */
+    @Override
+    public List<CityVo> getHomeList() {
+        LambdaQueryWrapper<Area> citySql = new LambdaQueryWrapper<>();
+        citySql.eq(Area::getLevel, 2);
+        List<Area> homeListDB = list(citySql);
+        homeListDB.stream()
+                .forEach(city -> {
+                    if (PROVINCE_CODE_LIST.contains(city.getCode())) {
+                        String cityName = CityCodeTypeEnum.getCityCodeType(city.getCode());
+                        city.setName(cityName);
+                    }
+                });
+        List<CityVo> cityList = cityConvert(homeListDB);
+        cityList = cityList.stream()
+                .filter(cityDTO -> ObjectUtils.isNotEmpty(cityDTO.getCityList()) && !cityDTO.getCityList().isEmpty())
+                .collect(Collectors.toList());
+        return cityList;
+    }
+
+    @Override
+    public List<CityVo> getCityName(String name) {
+        LambdaQueryWrapper<Area> citySql = new LambdaQueryWrapper<>();
+        citySql.like(Area::getName, name);
+        List<Area> homeList = list(citySql);
+        List<CityVo> cityList = cityConvert(homeList);
+        cityList = cityList.stream()
+                .filter(cityVO -> ObjectUtils.isNotEmpty(cityVO.getCityList()) && !cityVO.getCityList().isEmpty())
+                .collect(Collectors.toList());
+        return cityList;
+    }
+
+    private List<CityVo> cityConvert(List<Area> homeListDB) {
+        List<Area> homeList = homeListDB.stream()
+                .filter(city -> !CITY_TYPE.equals(city.getName()))
+                .collect(Collectors.toList());
+        Map<String, Area> cityMap = homeList.stream().collect(Collectors.toMap(Area::getName, Area -> Area, (existing, replacement) -> existing));
+//        }
+        //获取所有城市名称
+        List<String> cityList = cityMap.keySet().stream().collect(Collectors.toList());
+
+        List<CityVo> cityVoList = new ArrayList<>();
+        try {
+            //按照首字母进行城市分组
+            Map<String, List<String>> pinyingMap = CityChineseUtil.getCodeGroup(cityList);
+            //将分组对象转化成前端所需格式类型
+            pinyingMap.forEach((pinying, mapCityList) -> {
+                CityVo cityVo = new CityVo();
+                cityVo.setLetter(pinying);
+                List<CityVo.City> cityNewList = new ArrayList<>();
+                mapCityList.stream()
+                        .forEach(city -> {
+                            CityVo.City cityNew = new CityVo.City();
+                            BeanUtils.copyProperties(cityMap.get(city), cityNew);
+                            cityNewList.add(cityNew);
+                        });
+                cityVo.setCityList(cityNewList);
+                cityVoList.add(cityVo);
+            });
+        } catch (Exception e) {
+            log.error("首页城市列表拼音转换接口报错!");
+            throw new RuntimeException(e);
+        }
+        return cityVoList;
+    }
 }

+ 27 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/MaTechnicianServiceImpl.java

@@ -1,7 +1,12 @@
 package com.ylx.massage.service.impl;
 
 import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ylx.massage.domain.dto.MassageRecommendDto;
 import com.ylx.massage.domain.vo.MaTechnicianAppAddVo;
+import com.ylx.massage.domain.vo.MassageRecommendVo;
+import com.ylx.massage.domain.vo.MerchantVo;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -116,4 +121,26 @@ public class MaTechnicianServiceImpl implements IMaTechnicianService
     {
         return maTechnicianMapper.deleteMaTechnicianById(id);
     }
+
+    /**
+     * 首页选中的城市是否有开通服务
+     * @param areaCode
+     * @return
+     */
+    @Override
+    public Boolean isHasMerchantCity(String areaCode) {
+        LambdaQueryWrapper<MaTechnician> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(MaTechnician::getTeAreaCode, areaCode);
+        return maTechnicianMapper.selectCount(queryWrapper) > 0;
+    }
+
+    /**
+     * 首页按摩推荐
+     * @param dto
+     * @return
+     */
+    @Override
+    public List<MerchantVo> getMerchantRecommend(MassageRecommendDto dto) {
+        return maTechnicianMapper.getMerchantRecommend(dto);
+    }
 }

+ 30 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/MassageRecommendServiceImpl.java

@@ -0,0 +1,30 @@
+package com.ylx.massage.service.impl;
+
+import com.ylx.massage.domain.dto.MassageRecommendDto;
+import com.ylx.massage.domain.vo.MassageRecommendVo;
+import com.ylx.massage.domain.vo.MerchantVo;
+import com.ylx.massage.service.IMaTechnicianService;
+import com.ylx.massage.service.IMassageRecommendService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 类描述:用户首页按摩推荐
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/3 9:30
+ */
+@Service("massageRecommendService")
+public class MassageRecommendServiceImpl implements IMassageRecommendService {
+    @Autowired
+    private IMaTechnicianService iMaTechnicianService;
+    @Override
+    public List<MassageRecommendVo> getMassageRecommend(MassageRecommendDto dto) {
+        //按摩商户推荐
+        List<MerchantVo> merchantVoList = iMaTechnicianService.getMerchantRecommend(dto);
+       return null;
+    }
+}

+ 46 - 0
nightFragrance-massage/src/main/java/com/ylx/order/domain/OrderStatusFlow.java

@@ -0,0 +1,46 @@
+package com.ylx.order.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 com.ylx.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 类描述:订单状态流转表
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/3 10:30
+ */
+@Data
+@TableName(value = "t_order_status_flow", autoResultMap = true)
+@ApiModel(value = "OrderStatusFlow", description = "订单状态流转表")
+public class OrderStatusFlow extends BaseEntity implements Serializable  {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty("主键")
+    private Long id;
+    /*
+     * 订单号
+     */
+    @TableField("order_no")
+    @ApiModelProperty("订单号")
+    private String orderNo;
+    /**
+     * 流转状态
+     */
+    @TableField("status")
+    @ApiModelProperty("同步订单表状态")
+    private Integer status;
+
+
+}

+ 7 - 0
nightFragrance-massage/src/main/java/com/ylx/order/mapper/OrderStatusFlowMapper.java

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

+ 15 - 0
nightFragrance-massage/src/main/java/com/ylx/order/service/OrderStatusFlowService.java

@@ -0,0 +1,15 @@
+package com.ylx.order.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ylx.order.domain.OrderStatusFlow;
+
+public interface OrderStatusFlowService extends IService<OrderStatusFlow> {
+
+    /**
+     * 记录订单状态流转
+     * @param orderNo
+     * @param status
+     * @return
+     */
+    public void recordFlow(String orderNo, Integer status);
+}

+ 25 - 0
nightFragrance-massage/src/main/java/com/ylx/order/service/OrderStatusFlowServiceImpl.java

@@ -0,0 +1,25 @@
+package com.ylx.order.service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ylx.order.domain.OrderStatusFlow;
+import com.ylx.order.mapper.OrderStatusFlowMapper;
+import org.springframework.stereotype.Service;
+
+/**
+ * 类描述:
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/3 10:51
+ */
+@Service("orderStatusFlowService")
+public class OrderStatusFlowServiceImpl extends ServiceImpl<OrderStatusFlowMapper, OrderStatusFlow> implements OrderStatusFlowService {
+
+    @Override
+    public void recordFlow(String orderNo, Integer status) {
+        OrderStatusFlow orderStatusFlow = new OrderStatusFlow();
+        orderStatusFlow.setOrderNo(orderNo);
+        orderStatusFlow.setStatus(status);
+        this.save(orderStatusFlow);
+    }
+}

+ 3 - 2
nightFragrance-massage/src/main/java/com/ylx/shopingfundsdetail/controller/ShoppingFundsDetailController.java

@@ -9,6 +9,7 @@ import com.ylx.shopingfundsdetail.service.ShoppingFundsDetailService;
 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.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -33,14 +34,14 @@ public class ShoppingFundsDetailController {
 
     @ApiOperation("查询购物金明细")
     @PostMapping(value="queryShoppingFundsDetail")
-    public R<Page<ShoppingFundsDetailQueryVo>> queryShoppingFundsDetail(@RequestBody ShoppingFundsDetailQueryDto shoppingFundsDetailQueryDto) {
+    public R<Page<ShoppingFundsDetailQueryVo>> queryShoppingFundsDetail(@Validated @RequestBody ShoppingFundsDetailQueryDto shoppingFundsDetailQueryDto) {
         Page<ShoppingFundsDetailQueryVo> v =  shoppingFundsDetailService.queryShoppingFundsDetail(shoppingFundsDetailQueryDto);
         return R.ok(v);
     }
 
     @ApiOperation("新增购物金明细")
     @PostMapping(value="addShoppingFundsDetail")
-    public R<?> addShoppingFundsDetail(@RequestBody ShoppingFundsDetailAddDto dto) {
+    public R<?> addShoppingFundsDetail(@Validated  @RequestBody ShoppingFundsDetailAddDto dto) {
         shoppingFundsDetailService.addShoppingFundsDetail(dto);
         return R.ok();
     }

+ 2 - 1
nightFragrance-massage/src/main/java/com/ylx/shoppingfunds/controller/MyShoppingFundsController.java

@@ -7,6 +7,7 @@ import com.ylx.shoppingfunds.domain.vo.MyShoppingFundsQueryVo;
 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;
@@ -35,7 +36,7 @@ public class MyShoppingFundsController {
 
     @ApiOperation("更新购物金")
     @PostMapping(value="updateMyShoppingFunds")
-    public R<?> updateMyShoppingFunds(@RequestBody MyShoppingFundsUpdateDto myShoppingFundsUpdateDto) {
+    public R<?> updateMyShoppingFunds(@Validated  @RequestBody MyShoppingFundsUpdateDto myShoppingFundsUpdateDto) {
        wxUserService.updateMyShoppingFunds(myShoppingFundsUpdateDto.getWxUserId(), myShoppingFundsUpdateDto.getAmount(), myShoppingFundsUpdateDto.getExpenseType());
         return R.ok();
     }

+ 28 - 0
nightFragrance-massage/src/main/java/com/ylx/userhome/area/constants/CityCodeTypeEnum.java

@@ -0,0 +1,28 @@
+package com.ylx.userhome.area.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 平台订单状态
+ */
+@AllArgsConstructor
+@Getter
+public enum CityCodeTypeEnum {
+    TYPE_110100("110100","北京市"),
+    TYPE_120100("120100","天津市"),
+    TYPE_310100("310100","上海市"),
+    TYPE_500100("500100","重庆市");
+
+    private final String code;
+    private final String name;
+
+    public static String getCityCodeType(String code) {
+        for (CityCodeTypeEnum roleConstants : CityCodeTypeEnum.values()) {
+            if (roleConstants.getCode().equals(code)) {
+                return roleConstants.name;
+            }
+        }
+        return null;
+    }
+}

+ 73 - 0
nightFragrance-massage/src/main/java/com/ylx/userhome/area/controller/CustomerCityController.java

@@ -0,0 +1,73 @@
+package com.ylx.userhome.area.controller;
+
+import com.ylx.common.core.domain.R;
+import com.ylx.massage.service.AreaService;
+import com.ylx.massage.service.IMaTechnicianService;
+import com.ylx.userhome.area.domain.vo.CityVo;
+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 java.util.List;
+
+/**
+ * 类描述:用户首页选择地区
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/2 9:33
+ */
+@Slf4j
+@RestController
+@Api(tags = {"用户首页选择地区"})
+@RequestMapping("/customer/city")
+public class CustomerCityController {
+    @Resource(name = "areaService")
+    private AreaService areaService;
+
+    @Autowired
+    private IMaTechnicianService iMaTechnicianService;
+    /**
+     * 首页-城市列表
+     *
+     * @return
+     */
+    @ApiOperation(value="首页-城市列表", notes="首页-城市列表")
+    @PostMapping(value = "/getCityList")
+    public R<List<CityVo>> getCityList() {
+        try {
+            List<CityVo> list = areaService.getHomeList();
+            return R.ok(list);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return R.fail("接口异常请重试!");
+        }
+    }
+
+    @ApiOperation(value="首页选中的城市是否有开通服务", notes="首页选中的城市是否有开通服务;true表示该城市有开通服务")
+    @GetMapping(value = "/isHasMerchantCity")
+    public R<Boolean> isHasMerchantCity(@RequestParam("areaCode") String areaCode) {
+            return R.ok(iMaTechnicianService.isHasMerchantCity(areaCode));
+    }
+
+    /**
+     * 首页-城市列表查询
+     *
+     * @param name
+     * @return Result
+     */
+    @ApiOperation(value="首页-城市列表查询", notes="首页-城市列表查询")
+    @PostMapping(value = "/getCityByName")
+    public R<List<CityVo>> getCityByName(@RequestParam(value = "name",required = false) String name) {
+        try {
+            List<CityVo> list = areaService.getCityName(name);
+            return R.ok(list);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return R.fail("接口异常请重试!");
+        }
+    }
+}

+ 49 - 0
nightFragrance-massage/src/main/java/com/ylx/userhome/area/domain/vo/CityVo.java

@@ -0,0 +1,49 @@
+package com.ylx.userhome.area.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 用户首页查询地区返回vo
+ */
+@Data
+public class CityVo implements Serializable {
+
+    private static final long serialVersionUID = -7916708029160827482L;
+
+
+    /**
+     * 首字母
+     */
+    private String letter;
+    /**
+     * 城市集合
+     */
+    private List<City> cityList;
+
+    @Data
+    public static class City {
+        /**
+         * 区域主键
+         */
+        @ApiModelProperty(value = "区域ID")
+        private String code;
+        /**
+         * 区域名称
+         */
+        @ApiModelProperty(value = "区域名称")
+        private String name;
+
+        public City(String code, String name) {
+            this.code = code;
+            this.name = name;
+        }
+
+        public City() {
+
+        }
+    }
+}

+ 76 - 0
nightFragrance-massage/src/main/java/com/ylx/userhome/area/util/CityChineseUtil.java

@@ -0,0 +1,76 @@
+package com.ylx.userhome.area.util;
+
+import com.github.stuxuhai.jpinyin.PinyinException;
+import com.github.stuxuhai.jpinyin.PinyinFormat;
+import com.github.stuxuhai.jpinyin.PinyinHelper;
+
+import java.text.Collator;
+import java.util.*;
+
+/**
+ * @Description
+ * @Author lgx
+ * @Time 2023/4/17
+ */
+public class CityChineseUtil {
+
+    /**
+     * 获取姓名全拼和首字母
+     * PinyinFormat:WITH_TONE_MARK(声调模式),WITH_TONE_NUMBER(数字代表声调模式),WITHOUT_TONE(无声调模式)
+     * @param  chinese 汉语名称
+     * @return fullPinyin : 全拼   simplePinyin : 首字母  groupPinyin:微信用户组第一个字母
+     * @throws PinyinException
+     */
+    public static Map<String, String> changeChinesePinyin(String chinese) throws Exception {
+        Map<String, String> pinyin = new HashMap<String, String>();
+
+        //将中文转化成拼音(全拼)
+        String fullPinyin = PinyinHelper.convertToPinyinString(chinese,"", PinyinFormat.WITHOUT_TONE);
+        //将中文转化为简拼模式(各汉字第一个字母组成)
+        String simplePinyin = PinyinHelper.getShortPinyin(chinese);
+        //首字母
+        String firstPinyin = String.valueOf(simplePinyin.charAt(0));
+
+        pinyin.put("fullPinyin", fullPinyin);
+        pinyin.put("simplePinyin", simplePinyin.toUpperCase());
+        pinyin.put("groupPinyin", firstPinyin.toUpperCase());
+        return pinyin;
+    }
+    /**
+     * 按拼音首字母分组
+     * @param list
+     * @return
+     */
+    public static Map<String, List<String>> getCodeGroup(List<String> list) throws Exception{
+        Comparator<Object> com = Collator.getInstance(Locale.CHINA);
+        //按字母排序
+        Collections.sort(list, com);
+        //输出26个字母
+        Map<String, List<String>> map = new TreeMap<String, List<String>>();
+        for(int i=1;i<=26;i++){
+            String word = String. valueOf((char) (96 + i)). toUpperCase();
+            //循环找出首字母一样的数据
+            List<String> letter = new ArrayList<String>();
+            for (String str : list) {
+                String code = changeChinesePinyin(str).get("groupPinyin");
+                if(word.equals(code)) {
+                    letter.add(str);
+                }
+            }
+            map.put(word, letter);
+        }
+        return map;
+    }
+
+//        String str = "长沙市";
+//        //设置声调表示格式
+//        System.out.println(PinyinHelper.convertToPinyinString(str, ",", PinyinFormat.WITH_TONE_MARK)); // nǐ,hǎo,shì,jiè
+//        //数字表示声调
+//        System.out.println(PinyinHelper.convertToPinyinString(str, ",", PinyinFormat.WITH_TONE_NUMBER)); // ni3,hao3,shi4,jie4
+//        //无声调
+//        System.out.println( PinyinHelper.convertToPinyinString(str, ",", PinyinFormat.WITHOUT_TONE)); // ni,hao,shi,jie
+//        //获取拼音首字母
+//        System.out.println(PinyinHelper.getShortPinyin(str)); // nhsj
+//        //判断是否多音字
+//        System.out.println(PinyinHelper.hasMultiPinyin('啊'));//true
+}

+ 21 - 1
nightFragrance-massage/src/main/resources/mapper/giftCard/GiftCardMapper.xml

@@ -4,4 +4,24 @@
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ylx.giftCard.mapper.GiftCardMapper">
 
-</mapper>
+    <select id="selectManageList" resultType="com.ylx.giftCard.domain.GiftCard">
+        select *
+        from gift_card
+        <where>
+            is_delete = 0
+            <if test="dto != null and dto.name != null and dto.name != ''">
+                and name like concat('%', #{dto.name}, '%')
+            </if>
+            <if test="dto != null and dto.isPublished != null">
+                and is_published = #{dto.isPublished}
+            </if>
+            <if test="beginCreateTime != null">
+                and create_time &gt;= #{beginCreateTime}
+            </if>
+            <if test="endCreateTime != null">
+                and create_time &lt;= #{endCreateTime}
+            </if>
+        </where>
+        order by create_time desc
+    </select>
+</mapper>

+ 31 - 0
nightFragrance-massage/src/main/resources/mapper/massage/MaTechnicianMapper.xml

@@ -126,4 +126,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+     <!-- 首页按摩商户推荐列表-->
+    <select id="getMerchantRecommend" resultType="com.ylx.massage.domain.vo.MerchantVo">
+        SELECT
+        t.id AS merchantId,
+        t.te_name AS teName,
+        t.n_star AS nStar,
+        t.n_num AS nNum,
+        p.price AS price,
+        ST_Distance_Sphere(
+        POINT(a.longitude, a.latitude),
+        POINT(#{dto.longitude}, #{dto.latitude})
+        ) / 1000 AS distance
+        FROM ma_technician t
+        LEFT JOIN t_address a ON t.id = a.merchant_id
+        LEFT JOIN(
+            SELECT p.user_id AS user_id ,
+                   p.project_current_price AS  price
+            FROM ma_project p
+            WHERE p.is_delete = 0
+            ORDER BY p.project_current_price ASC
+            LIMIT 1
+        ) p ON t.id = p.user_d
+
+        WHERE
+        t.is_delete = 0
+        and a.user_type = 2
+        and a.type = 1
+        and a.is_delete = 0
+        ORDER BY t.n_num DESC
+        LIMIT 5
+    </select>
 </mapper>

+ 9 - 0
nightFragrance-massage/src/main/resources/mapper/order/OrderStatusFlowMapper.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ylx.order.mapper.OrderStatusFlowMapper">
+
+
+
+</mapper>

+ 6 - 0
pom.xml

@@ -30,6 +30,7 @@
         <poi.version>4.1.2</poi.version>
         <velocity.version>2.3</velocity.version>
         <jwt.version>0.9.1</jwt.version>
+        <jpinyin.version>1.1.8</jpinyin.version>
     </properties>
 
     <!-- 依赖声明 -->
@@ -162,6 +163,11 @@
                 <artifactId>nightFragrance-common</artifactId>
                 <version>${ruoyi.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.github.stuxuhai</groupId>
+                <artifactId>jpinyin</artifactId>
+                <version>${jpinyin.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>