phpcjl
2024-11-26 029af9c4503d6838bddd55a6ecd6b73705ca1522
1.完善接口的定义
2.完成部分接口的开发
3.集成基于Redisson的分布式锁
12个文件已修改
8个文件已添加
587 ■■■■■ 已修改文件
ruoyi-api/ruoyi-api-order/src/main/java/model/Order.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TechnicianSubscribe.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/annotation/DistributedLock.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/aspectj/DistributedLockAspect.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/UserPointController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/UserPointService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/UserPointServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/vo/UserPointDetailVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/vo/UserPointVO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/OrderService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/OrderDetailVO.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/OrderGoodsVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/OrderVO.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TechnicianSubscribeController.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/distributedservice/DistributedTechnicianService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TechnicianSubscribeService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TechnicianSubscribeServiceImpl.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/model/Order.java
@@ -84,6 +84,10 @@
    @TableField("express_json")
    private String expressJson;
    @ApiModelProperty(value = "收货地址信息")
    @TableField("address_json")
    private String addressJson;
    @ApiModelProperty(value = "平台分佣")
    @TableField("share_amount")
    private BigDecimal shareAmount;
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TechnicianSubscribe.java
@@ -38,7 +38,7 @@
    @ApiModelProperty(value = "技师id")
    @TableField("technician_id")
    private Integer technicianId;
    private Long technicianId;
    @ApiModelProperty(value = "预约时间")
    @TableField("subscribe_time")
ruoyi-common/ruoyi-common-redis/pom.xml
@@ -22,6 +22,12 @@
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.10.1</version>
        </dependency>
        
        <!-- RuoYi Common Core-->
        <dependency>
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/annotation/DistributedLock.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.common.redis.annotation;
import java.lang.annotation.*;
/**
 * @Descreption: 分布式锁注解
 * @Author: luofl
 * @Date: 2024/11/26 16:43
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
    /**
     * 锁名字(没有EL解析)
     */
    String lockName() default "";
    /**
     * 锁前缀(有EL解析)
     */
    String lockNamePre() default "";
    /**
     * 锁后缀(有EL解析)
     */
    String lockNamePost() default "";
    /**
     * 锁前后缀拼接分隔符
     */
    String separator() default "_";
}
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/aspectj/DistributedLockAspect.java
New file
@@ -0,0 +1,142 @@
package com.ruoyi.common.redis.aspectj;
import com.ruoyi.common.redis.annotation.DistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Objects;
/**
 * @Description 分布式锁切面
 * 注意!!!分布式锁不能加在事务方法当中:因为当锁释放,事务还没有提交
 * @Author luofl
 * @Date 2024/11/26 16:42
 */
@Aspect
@Component
@Slf4j
public class DistributedLockAspect {
    @Resource
    private RedissonClient redissonClient;
    /**
     * @Descreption: 定义切面:以注解为切面
     * @Author: luofl
     * @Date: 2024/11/26 16:44
     */
    @Pointcut("@annotation(com.ruoyi.common.redis.annotation.DistributedLock)")
    public void distributedLockAspect() {
    }
    @Around(value = "distributedLockAspect()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        //切点所在的类
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        //
        DistributedLock annotation = method.getAnnotation(DistributedLock.class);
        String lockName = getLockName(annotation, pjp.getArgs(), method);
        //log.info("lockName:"+lockName);
        RLock lock = redissonClient.getLock(lockName);
        lock.lock();
        try {
            return pjp.proceed();
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                //释放锁
                lock.unlock();
            }
        }
    }
    /**
     * @Descreption: 获取锁名字,优先获取注解中锁名
     * @Author: luofl
     * @Date: 2024/11/26 16:45
     */
    private String getLockName(DistributedLock distributedLock, Object[] args, Method method) {
        //优先获取注解名称
        if (StringUtils.isNotBlank(distributedLock.lockName())) {
            return distributedLock.lockName();
        }
        //根据参数匹配有参数就使用动态参数,没有就使用定义参数
        String lockNamePre = distributedLock.lockNamePre();
        String lockNamePost = distributedLock.lockNamePost();
        String separator = distributedLock.separator();
        String preExpression = parseExpression(lockNamePre, method, args);
        String postExpression = parseExpression(lockNamePost, method, args);
        StringBuilder sb = new StringBuilder();
        if (StringUtils.isNotBlank(preExpression)) {
            sb.append(preExpression);
        } else {
            sb.append(lockNamePre);
        }
        sb.append(separator);
        if (StringUtils.isNotBlank(postExpression)) {
            sb.append(postExpression);
        } else {
            sb.append(lockNamePost);
        }
        return sb.toString();
    }
    /**
     * el表达式解析
     *
     * @param expressionString 解析值
     * @param method           方法
     * @param args             参数
     */
    private String parseExpression(String expressionString, Method method, Object[] args) {
        //获取被拦截方法参数名列表
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNameArr = discoverer.getParameterNames(method);
        //SPEL解析
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < Objects.requireNonNull(paramNameArr).length; i++) {
            context.setVariable(paramNameArr[i], args[i]);
        }
        return parser.parseExpression(expressionString).getValue(context, String.class);
    }
    //    ==========================示例=============================
    //固定静态参数锁:product_lock
    @DistributedLock(lockName = "product_lock")
    @GetMapping(value = "/test1")
    public void test1() {
        System.out.println("执行事务");
    }
    //未匹配到参数,因此仍然是静态参数锁:#param1_#param2
    @DistributedLock(lockNamePre = "#param1", lockNamePost = "#param2")
    @GetMapping(value = "/test2")
    public void test2() {
        System.out.println("执行事务");
    }
    //匹配到参数,动态参数锁:hello_world
    @DistributedLock(lockNamePre = "#order", lockNamePost = "#param2")
    @GetMapping(value = "/test3")
    public void test3(String param1, String param2) {
        System.out.println("执行事务");
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/UserPointController.java
@@ -1,9 +1,23 @@
package com.ruoyi.account.controller;
import com.ruoyi.account.service.UserPointService;
import com.ruoyi.account.vo.UserPointDetailVO;
import com.ruoyi.account.vo.UserPointVO;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.security.utils.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
@@ -16,10 +30,39 @@
@RestController
@RequestMapping("/user-point")
@Api("用户积分")
public class UserPointController {
public class UserPointController extends BaseController {
    @Resource
    private UserPointService userPointService;
    /**
     * 积分
     * 获取个人积分
     */
    @GetMapping("/getUserPoint")
    @ApiOperation("获取个人积分")
    public R<UserPointVO> getUserPoint(){
        return R.ok(userPointService.getUserPoint(SecurityUtils.getUserId()));
    }
    /**
     * 获取变更明细
     */
    @GetMapping("/getUserPointDetail")
    @ApiOperation("获取变更明细")
    public R<UserPointDetailVO> getUserPointDetail(@ApiParam("指定日期") LocalDateTime date,
                                                   @ApiParam("变动类型(1=消费积分,2=返佣积分,3=拉新人积分,4=兑换商品 " +
                                                           "5 = 门店业绩积分 6 =门店返佣积分7=技师业绩积分8 =转赠积分 9 =做工积分 " +
                                                           "10 =注册积分)") Integer type){
        return R.ok();
    }
    /**
     * 转赠积分
     */
    @PostMapping("/transferPoint")
    @ApiOperation("转赠积分")
    public R<Void> transferPoint(@ApiParam("积分") BigDecimal point, @ApiParam("手机号") Long phone){
        return R.ok();
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/UserPointService.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.account.api.model.UserPoint;
import com.ruoyi.account.vo.UserPointVO;
/**
 * <p>
@@ -13,4 +14,5 @@
 */
public interface UserPointService extends IService<UserPoint> {
    UserPointVO getUserPoint(Long userId);
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/UserPointServiceImpl.java
@@ -4,6 +4,7 @@
import com.ruoyi.account.mapper.UserPointMapper;
import com.ruoyi.account.api.model.UserPoint;
import com.ruoyi.account.service.UserPointService;
import com.ruoyi.account.vo.UserPointVO;
import org.springframework.stereotype.Service;
/**
@@ -17,4 +18,8 @@
@Service
public class UserPointServiceImpl extends ServiceImpl<UserPointMapper, UserPoint> implements UserPointService {
    @Override
    public UserPointVO getUserPoint(Long userId) {
        return null;
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/vo/UserPointDetailVO.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.account.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@ApiModel(value="UserPointDetail对象", description="")
public class UserPointDetailVO {
    @ApiModelProperty(value = "变动类型(1=消费积分,2=返佣积分,3=拉新人积分,4=兑换商品 5 = 门店业绩积分 6 =门店返佣积分7=技师业绩积分8 =转赠积分 9 =做工积分 10 =注册积分)")
    private Integer type;
    @ApiModelProperty(value = "变动金额")
    private BigDecimal variablePoint;
    @ApiModelProperty(value = "变动时间")
    private LocalDateTime createTime;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/vo/UserPointVO.java
New file
@@ -0,0 +1,31 @@
package com.ruoyi.account.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
@ApiModel(value="UserPoint对象", description="")
public class UserPointVO {
    @ApiModelProperty(value = "总积分")
    private Integer totalPoint;
    @ApiModelProperty(value = "消费积分数")
    private BigDecimal shopPoint;
    @ApiModelProperty(value = "返佣积分数")
    private BigDecimal sharePoint;
    @ApiModelProperty(value = "拉新积分")
    private BigDecimal pullNewPoint;
    @ApiModelProperty(value = "门店业绩积分")
    private BigDecimal shopAchievementPoint;
    @ApiModelProperty(value = "门店返佣积分")
    private BigDecimal shopSharePoint;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java
@@ -1,8 +1,10 @@
package com.ruoyi.order.controller;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.order.service.OrderService;
import com.ruoyi.order.vo.OrderDetailVO;
import com.ruoyi.order.vo.OrderVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -13,6 +15,7 @@
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
 * <p>
@@ -31,6 +34,30 @@
    /**
     * 我的订单列表
     */
    @ApiOperation(value = "我的订单列表", tags = {"小程序-个人中心-我的订单"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单状态", name = "status", required = true, dataType = "int"),
    })
    @GetMapping("/list/{status}")
    public R<List<OrderVO>> list(@PathVariable("status") Integer status){
        return R.ok(orderService.getOrderList(status));
    }
    /**
     * 订单详情
     */
    @ApiOperation(value = "订单详情", tags = {"小程序-订单详情"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单id", name = "orderId", required = true, dataType = "int"),
    })
    @GetMapping("/detail/{orderId}")
    public R<OrderDetailVO> detail(@PathVariable("orderId") Long orderId){
        return R.ok(orderService.getOrderDetail(orderId));
    }
    /**
     * 扫码校验
     */
    @ApiOperation(value = "扫码校验", tags = {"小程序-个人中心-门店管理-扫码核销校验"})
@@ -38,21 +65,35 @@
            @ApiImplicitParam(value = "分享id", name = "shareId", required = true, dataType = "int"),
    })
    @GetMapping("/check/{orderId}/{shopId}")
    public AjaxResult check(@PathVariable("orderId") Integer orderId, @PathVariable("shopId") Integer shopId){
        return AjaxResult.ok(orderService.check(orderId, shopId));
    public R<Boolean> check(@PathVariable("orderId") Long orderId, @PathVariable("shopId") Long shopId){
        return R.ok(orderService.check(orderId, shopId));
    }
    /**
     * 订单详情
     * 订单核销
     */
    @ApiOperation(value = "订单详情", tags = {"订单详情"})
    @ApiOperation(value = "订单核销", tags = {"小程序-个人中心-门店管理-扫码核销"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "核销码", name = "code", required = true, dataType = "String"),
    })
    @GetMapping("/writeOff/{code}")
    public R<Void> writeOff(@PathVariable("code") String code){
        return R.ok();
    }
    /**
     * 取消订单
     */
    @ApiOperation(value = "取消订单", tags = {"小程序-个人中心-我的订单-取消订单"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单id", name = "orderId", required = true, dataType = "int"),
    })
    @GetMapping("/detail/{orderId}")
    public AjaxResult detail(@PathVariable("orderId") Integer orderId){
        return AjaxResult.success(orderService.getById(orderId));
    @GetMapping("/cancel/{orderId}")
    public R<Void> cancel(@PathVariable("orderId") Long orderId){
        return R.ok();
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/OrderService.java
@@ -1,7 +1,11 @@
package com.ruoyi.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.order.vo.OrderDetailVO;
import com.ruoyi.order.vo.OrderVO;
import model.Order;
import java.util.List;
/**
 * <p>
@@ -12,5 +16,9 @@
 * @since 2024-11-21
 */
public interface OrderService extends IService<Order> {
    boolean check(Integer orderId, Integer shopId);
    List<OrderVO> getOrderList(Integer status);
    OrderDetailVO getOrderDetail(Long orderId);
    boolean check(Long orderId, Long shopId);
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderServiceImpl.java
@@ -3,8 +3,13 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.order.mapper.OrderMapper;
import com.ruoyi.order.service.OrderService;
import com.ruoyi.order.vo.OrderDetailVO;
import com.ruoyi.order.vo.OrderVO;
import model.Order;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
/**
 * <p>
@@ -18,7 +23,18 @@
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Override
    public boolean check(Integer orderId, Integer shopId) {
    public List<OrderVO> getOrderList(Integer status) {
        return Collections.emptyList();
    }
    @Override
    public OrderDetailVO getOrderDetail(Long orderId) {
        return null;
    }
    @Override
    public boolean check(Long orderId, Long shopId) {
        // TODO 待实现
        return false;
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/OrderDetailVO.java
New file
@@ -0,0 +1,61 @@
package com.ruoyi.order.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
@ApiModel(value = "订单明细")
public class OrderDetailVO {
    @ApiModelProperty(value = "订单id")
    private Long id;
    @ApiModelProperty(value = "订单商品")
    private List<OrderGoodsVO> goodsList;
    @ApiModelProperty(value = "使用积分")
    private BigDecimal point;
    @ApiModelProperty(value = "收货地址id")
    private Long addressId;
    @ApiModelProperty(value = "收货联系人")
    private String recieveName;
    @ApiModelProperty(value = "收货联系电话")
    private String recievePhone;
    @ApiModelProperty(value = "收货地址")
    private String recieveAddress;
    @ApiModelProperty(value = "订单编号")
    private String orderNumber;
    @ApiModelProperty("下单时间")
    private String createTime;
    @ApiModelProperty(value = "订单总金额")
    private BigDecimal totalAmount;
    @ApiModelProperty(value = "优惠券名称")
    private String couponName;
    @ApiModelProperty(value = "参与活动名称")
    private String activityName;
    @ApiModelProperty(value = "抵扣金额")
    private BigDecimal couponAmount;
    @ApiModelProperty(value = "快递费")
    private BigDecimal expressAmount;
    @ApiModelProperty(value = "可获得积分")
    private BigDecimal pointAmount;
    @ApiModelProperty(value = "实际支付价格")
    private BigDecimal paymentAmount;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/OrderGoodsVO.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.order.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
@ApiModel(value = "订单商品")
public class OrderGoodsVO {
    @ApiModelProperty(value = "商品id")
    private Long goodsId;
    @ApiModelProperty(value = "商品名称")
    private String goodsName;
    @ApiModelProperty(value = "类型(1=服务商品,2=单品商品)")
    private Integer type;
    @ApiModelProperty(value = "数量")
    private Integer num;
    @ApiModelProperty(value = "商品图片")
    private String goodsPic;
    @ApiModelProperty(value = "基础售价")
    private BigDecimal sellingPrice;
    @ApiModelProperty(value = "划线价")
    private BigDecimal originalPrice;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/OrderVO.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.order.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class OrderVO {
    @ApiModelProperty(value = "订单id")
    private Long id;
    @ApiModelProperty(value = "订单编号")
    private String orderNumber;
    @ApiModelProperty(value = "1待发货2待收货3待使用4已完成待评论5已取消6已退款7售后中8已完成已评论")
    private Integer orderStatus;
    @ApiModelProperty(value = "商品图片")
    private List<String> goodsPics;
    @ApiModelProperty(value = "商品名称")
    private String goodsName;
    @ApiModelProperty(value = "使用积分")
    private BigDecimal point;
    @ApiModelProperty(value = "实际支付价格")
    private BigDecimal paymentAmount;
    @ApiModelProperty(value = "商品数量")
    private Integer num;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TechnicianSubscribeController.java
@@ -7,15 +7,13 @@
import com.ruoyi.common.core.web.page.TableDataInfo;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.other.api.domain.TechnicianSubscribe;
import com.ruoyi.other.distributedservice.DistributedTechnicianService;
import com.ruoyi.other.service.TechnicianSubscribeService;
import com.ruoyi.other.vo.TechnicianSubscribeVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@@ -34,6 +32,8 @@
public class TechnicianSubscribeController extends BaseController {
    @Resource
    private TechnicianSubscribeService technicianSubscribeService;
    @Resource
    private DistributedTechnicianService distributedTechnicianService;
    /**
     * 预约列表
@@ -49,6 +49,16 @@
    }
    /**
     * 预约技师
     */
    @PostMapping("/subscribe")
    @ApiOperation(value = "预约技师", notes = "预约技师", tags = {"小程序-个人中心-门店管理-预约列表-预约技师"})
    public R<Void> subscribe(@RequestBody TechnicianSubscribe technicianSubscribe){
        distributedTechnicianService.subscribe(technicianSubscribe,technicianSubscribe.getTechnicianId());
        return R.ok();
    }
    /**
     * 取消服务
     */
    @GetMapping("/cancel")
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/distributedservice/DistributedTechnicianService.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.other.distributedservice;
import com.ruoyi.common.redis.annotation.DistributedLock;
import com.ruoyi.other.api.domain.TechnicianSubscribe;
import com.ruoyi.other.service.TechnicianSubscribeService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
 * @Desecription: 技师业务分布式锁业务处理
 * 由于service被事务包裹,因此需要单独建立包来存放分布式锁业务,
 * 分布式锁不能在业务方法中执行
 * 事务隔离问题: 如果将分布式锁放在业务方法内部,并且业务方法处于事务中,那么在事务提交之前,分布式锁可能无法释放,导致其他事务无法获取到该锁,从而造成死锁或长时间的阻塞。
 * @Autor: luofl
 * @Date: 2024/11/26 16:50
 */
@Service
public class DistributedTechnicianService {
    @Resource
    private TechnicianSubscribeService technicianSubscribeService;
    @DistributedLock(lockNamePre = "#TECHNICIAN_SUBSCRIBE_LOCK", lockNamePost = "#technicianId")
    public void subscribe(TechnicianSubscribe technicianSubscribe,Long technicianId){
        technicianSubscribeService.subscribe(technicianSubscribe);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TechnicianSubscribeService.java
@@ -21,4 +21,5 @@
     */
    List<TechnicianSubscribeVO> getTechnicianSubscribeByUserAndShop(Long userId, Long shopId);
    void subscribe(TechnicianSubscribe technicianSubscribe);
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TechnicianSubscribeServiceImpl.java
@@ -1,19 +1,25 @@
package com.ruoyi.other.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.other.api.domain.Technician;
import com.ruoyi.other.mapper.TechnicianMapper;
import com.ruoyi.other.mapper.TechnicianSubscribeMapper;
import com.ruoyi.other.api.domain.TechnicianSubscribe;
import com.ruoyi.other.service.TechnicianSubscribeService;
import com.ruoyi.other.vo.TechnicianSubscribeVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Collections;
import java.time.LocalDateTime;
import java.util.List;
/**
 * <p>
 *  服务实现类
 * 服务实现类
 * </p>
 *
 * @author luodangjia
@@ -23,9 +29,32 @@
public class TechnicianSubscribeServiceImpl extends ServiceImpl<TechnicianSubscribeMapper, TechnicianSubscribe> implements TechnicianSubscribeService {
    @Resource
    private TechnicianSubscribeMapper technicianSubscribeMapper;
    @Resource
    private TechnicianMapper technicianMapper;
    @Override
    public List<TechnicianSubscribeVO> getTechnicianSubscribeByUserAndShop(Long userId, Long shopId) {
        return technicianSubscribeMapper.getTechnicianSubscribeByUserAndShop(userId, shopId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void subscribe(TechnicianSubscribe technicianSubscribe) {
        Long technicianId = technicianSubscribe.getTechnicianId();
        Technician technician = technicianMapper.selectOne(new LambdaQueryWrapper<Technician>()
                .eq(Technician::getId, technicianId)
                .eq(Technician::getStatus, 1)
                .eq(Technician::getSubscribeStatus, 1));
        if (null == technician) {
            throw new ServiceException("不满足预约条件");
        }
        Long userId = SecurityUtils.getUserId();
        TechnicianSubscribe subscribe = new TechnicianSubscribe();
        subscribe.setAppUserId(userId);
        subscribe.setDelFlag(0);
        subscribe.setCreateTime(LocalDateTime.now());
        technicianSubscribeMapper.insert(subscribe);
        technician.setSubscribeStatus(2);
        technicianMapper.updateById(technician);
    }
}