创建订单(内附源码)

页面预览

订单详情

image-20230227071834134

image-20230227071900964

订单列表

image-20230227071924297

第01章-创建订单

生成订单分析

  • 生成订单方法参数:就诊人id与 排班id
  • 生成订单需要获取就诊人信息(微服务远程调用service-user)
  • 获取排班信息与规则信息(微服务远程调用service-hosp)
  • 下单后,通过接口去医院预约下单(httpclient远程调用医院端的接口)
  • 下单成功更新排班信息并发送短信(向mq发送信息)

1、创建订单微服务

1.1、创建数据库

资料:资料>订单微服务>guigu_syt_order.sql

1.2、创建service-order微服务

在service模块下创建service-order模块

image-20230318102129088

1.3、添加依赖

在service-order中添加依赖:

<dependencies>
    <!--实体-->
    <dependency>
        <groupId>com.atguigu</groupId>
        <artifactId>model</artifactId>
        <version>1.0</version>
    </dependency>

    <!--服务通用配置-->
    <dependency>
        <groupId>com.atguigu</groupId>
        <artifactId>service-util</artifactId>
        <version>1.0</version>
    </dependency>

    <!--自定义安全模块-->
    <dependency>
        <groupId>com.atguigu</groupId>
        <artifactId>spring-security</artifactId>
        <version>1.0</version>
    </dependency>

    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    
    <!--时间日期工具-->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
    </dependency>

    <!-- 单元测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1.4、使用代码生成器

找到service-util模块中的代码生成器,修改moduleName为order,并执行,然后删除entity包,相关类中引入model模块中的类

1.5、创建配置文件

在server-order模块中resources目录下创建文件

application.yml

spring:
  application:
    name: service-order
  profiles:
    active: dev,redis

application-dev.yml

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:com/atguigu/syt/order/mapper/xml/*.xml
server:
  port: 8205
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    password: 123456
    url: jdbc:mysql://localhost:3306/guigu_syt_order?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
    username: root
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

logging:
  level:
    root: info
  file:
    path: order

feign:
  client:
    config:
      default:
        connect-timeout: 2000 #连接建立的超时时长,单位是ms,默认1s
        read-timeout: 2000 #处理请求的超时时间,单位是ms,默认为1s

  sentinel:
    enabled: true #开启Feign对Sentinel的支持

1.6、配置网关

在server-gateway中配置网关路由

        - id: service-order
          predicates: Path=/*/order/**
          uri: lb://service-order

1.7、创建启动类

package com.atguigu.syt.order;

@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"})
@EnableFeignClients("com.atguigu.syt")
public class ServiceOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceOrderApplication.class, args);
    }
}

2、获取就诊人接口

2.1、Controller

在service-user模块创建controller.inner包,创建InnerPatientController类

package com.atguigu.syt.user.controller.inner;

@Api(tags = "就诊人接口-供其他微服务远程调用")
@RestController
@RequestMapping("/inner/user/patient")
public class InnerPatientController {

    @Resource
    private PatientService patientService;

    @ApiOperation("获取就诊人")
    @ApiImplicitParam(name = "id",value = "就诊人id", required = true)
    @GetMapping("/get/{id}")
    public Patient getPatient(@PathVariable("id") Long id) {
        return patientService.getById(id);
    }
}

2.2、创建service-user-client模块

在service-client下创建service-user-client

service-client中添加依赖

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>model</artifactId>
    <version>1.0</version>
</dependency>

2.3、创建Feign接口

在service-user-client中创建接口

package com.atguigu.syt.user.client;

@FeignClient(
        value = "service-user",
        contextId = "patientFeignClient",
        fallback = PatientDegradeFeignClient.class)
public interface PatientFeignClient {

    /**
     * 获取就诊人
     * @param id
     * @return
     */
    @GetMapping("/inner/user/patient/get/{id}")
    Patient getPatient(@PathVariable Long id);
}

降级:

package com.atguigu.syt.user.client.impl;

@Component
public class PatientDegradeFeignClient implements PatientFeignClient {

    @Override
    public Patient getPatient(Long id) {
        return null;
    }
}

3、获取排班信息接口

3.1、Controller

在service-hosp模块创建controller.inner包,创建InnerScheduleController类

package com.atguigu.syt.hosp.controller.inner;

@Api(tags = "医院接口-供其他微服务远程调用")
@RestController
@RequestMapping("/inner/hosp/hospital")
public class InnerScheduleController {

    @Resource
    private ScheduleService scheduleService;

    @ApiOperation("根据排班id获取预约下单数据")
    @ApiImplicitParam(name = "scheduleId",value = "排班id", required = true)
    @GetMapping("/getScheduleOrderVo/{scheduleId}")
    public ScheduleOrderVo getScheduleOrderVo(@PathVariable String scheduleId) {
        return scheduleService.getScheduleOrderVo(scheduleId);
    }
}

3.2、Service

接口:ServiceScheduleService

/**
* 获取排班相关数据
* @param scheduleId
* @return
*/
ScheduleOrderVo getScheduleOrderVo(String scheduleId);

实现:ScheduleServiceImpl

@Override
public ScheduleOrderVo getScheduleOrderVo(String scheduleId) {

    Schedule schedule = this.getDetailById(scheduleId);
    String hosname = (String)schedule.getParam().get("hosname");
    String depname = (String)schedule.getParam().get("depname");

    ScheduleOrderVo scheduleOrderVo = new ScheduleOrderVo();
    scheduleOrderVo.setHoscode(schedule.getHoscode()); //医院编号
    scheduleOrderVo.setHosname(hosname); //医院名称
    scheduleOrderVo.setDepcode(schedule.getDepcode()); //科室编号
    scheduleOrderVo.setDepname(depname); //科室名称
    scheduleOrderVo.setHosScheduleId(schedule.getHosScheduleId()); //医院端的排班主键
    scheduleOrderVo.setAvailableNumber(schedule.getAvailableNumber()); //剩余预约数
    scheduleOrderVo.setTitle(hosname + depname + "挂号费");
    scheduleOrderVo.setReserveDate(schedule.getWorkDate()); //安排日期
    scheduleOrderVo.setReserveTime(schedule.getWorkTime()); //安排时间(0:上午 1:下午)
    scheduleOrderVo.setAmount(schedule.getAmount());//挂号费用

    //获取预约规则相关数据
    Hospital hospital = hospitalRepository.findByHoscode(schedule.getHoscode());
    BookingRule bookingRule = hospital.getBookingRule();
    String quitTime = bookingRule.getQuitTime();//退号时间
    //退号实际时间(如:就诊前一天为-1,当天为0)
    DateTime quitDay = new DateTime(schedule.getWorkDate()).plusDays(bookingRule.getQuitDay());//退号日期
    DateTime quitDateTime = this.getDateTime(quitDay, quitTime);//可退号的具体的日期和时间
    scheduleOrderVo.setQuitTime(quitDateTime.toDate());

    return scheduleOrderVo;
}

3.3、创建service-hosp-client模块

在service-client下创建service-hosp-client

3.4、创建Feign接口

在service-hosp-client中创建接口

package com.atguigu.syt.hosp.client;

@FeignClient(
    value = "service-hosp",
    contextId = "scheduleFeignClient",
    fallback = ScheduleDegradeFeignClient.class
)
public interface ScheduleFeignClient {
    /**
     * 根据排班id获取预约下单数据
     * @param scheduleId
     * @return
     */
    @GetMapping("/inner/hosp/hospital/getScheduleOrderVo/{scheduleId}")
    ScheduleOrderVo getScheduleOrderVo(@PathVariable("scheduleId") String scheduleId);
}

降级:

package com.atguigu.syt.hosp.client.impl;

@Component
public class ScheduleDegradeFeignClient implements ScheduleFeignClient {
    @Override
    public ScheduleOrderVo getScheduleOrderVo(String scheduleId) {
        return null;
    }
}

4、生成订单

4.1、service中引入依赖

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>service-user-client</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>service-hosp-client</artifactId>
    <version>1.0</version>
</dependency>

4.2、Controller

在service-order中创建controller.front包,创建FrontOrderInfoController类,定义创建订单接口

package com.atguigu.syt.order.controller.front;

@Api(tags = "订单接口")
@RestController
@RequestMapping("/front/order/orderInfo")
public class FrontOrderInfoController {

    @Resource
    private OrderInfoService orderInfoService;

    @Resource
    private AuthContextHolder authContextHolder;

    @ApiOperation("创建订单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "scheduleId",value = "排班id", required = true),
            @ApiImplicitParam(name = "patientId",value = "就诊人id", required = true)})
    @PostMapping("/auth/submitOrder/{scheduleId}/{patientId}")
    public Result<Long> submitOrder(@PathVariable String scheduleId, @PathVariable Long patientId, HttpServletRequest request, HttpServletResponse response) {

        authContextHolder.checkAuth(request, response);
        Long orderId = orderInfoService.submitOrder(scheduleId, patientId);
        return Result.ok(orderId);
    }
}

4.3、Service

接口:OrderInfoService

/**
     * 根据排班id和就诊人id创建订单
     * @param scheduleId
     * @param patientId
     * @return 新订单id
     */
Long submitOrder(String scheduleId, Long patientId);

实现:OrderInfoServiceImpl

@Resource
private PatientFeignClient patientFeignClient;

@Resource
private ScheduleFeignClient scheduleFeignClient;

@Override
public Long submitOrder(String scheduleId, Long patientId) {

    //就诊人数据:远程获取就诊人数据(openfeign)
    Patient patient = patientFeignClient.getPatient(patientId);
    if(patient == null) {
        throw new GuiguException(ResultCodeEnum.PARAM_ERROR);
    }

    //获取医院排班相关数据
    ScheduleOrderVo scheduleOrderVo = scheduleFeignClient.getScheduleOrderVo(scheduleId);
    if(scheduleOrderVo == null) {
        throw new GuiguException(ResultCodeEnum.PARAM_ERROR);
    }

    //SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(scheduleOrderVo.getHoscode());
    SignInfoVo signInfoVo = new SignInfoVo();
    signInfoVo.setSignKey("8af52af00baf6aec434109fc17164aae");
    signInfoVo.setApiUrl("http://localhost:9998");
    if(signInfoVo == null) {
        throw new GuiguException(ResultCodeEnum.PARAM_ERROR);
    }

    if(scheduleOrderVo.getAvailableNumber() <= 0) {
        throw new GuiguException(ResultCodeEnum.NUMBER_NO);
    }

    //创建订单对象
    OrderInfo orderInfo = new OrderInfo();

    //基本信息
    String outTradeNo = UUID.randomUUID().toString().replace("-", "");
    orderInfo.setOutTradeNo(outTradeNo); //订单号
    orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());//未支付

    //就诊人数据
    orderInfo.setPatientId(patientId);
    orderInfo.setPatientPhone(patient.getPhone());
    orderInfo.setPatientName(patient.getName());
    orderInfo.setUserId(patient.getUserId());

    //医院排班相关数据
    orderInfo.setScheduleId(scheduleId);
    BeanUtils.copyProperties(scheduleOrderVo, orderInfo);//拷贝相关属性

    //step4:调用医院端接口获取相关数据
    Map<String, Object> paramsMap = new HashMap<>();
    paramsMap.put("hoscode", scheduleOrderVo.getHoscode());
    paramsMap.put("depcode", scheduleOrderVo.getDepcode());
    paramsMap.put("hosScheduleId", scheduleOrderVo.getHosScheduleId());
    paramsMap.put(
        "reserveDate",
        new DateTime(scheduleOrderVo.getReserveDate()).toString("yyyy-MM-dd")
    );
    paramsMap.put("reserveTime", scheduleOrderVo.getReserveTime());
    paramsMap.put("amount", scheduleOrderVo.getAmount());
    paramsMap.put("name", patient.getName());
    paramsMap.put("certificatesType", patient.getCertificatesType());
    paramsMap.put("certificatesNo", patient.getCertificatesNo());
    paramsMap.put("sex", patient.getSex());
    paramsMap.put("birthdate", patient.getBirthdate());
    paramsMap.put("phone", patient.getPhone());
    paramsMap.put("isMarry", patient.getIsMarry());
    paramsMap.put("timestamp", HttpRequestHelper.getTimestamp());

    paramsMap.put("sign", HttpRequestHelper.getSign(paramsMap, signInfoVo.getSignKey()));//标准签名
    JSONObject jsonResult = HttpRequestHelper.sendRequest(
        paramsMap, signInfoVo.getApiUrl() + "/order/submitOrder"
    );
    log.info("结果:" + jsonResult.toJSONString());

    if(jsonResult.getInteger("code") != 200){//失败

        log.error("预约失败,"
                  + "code:" + jsonResult.getInteger("code")
                  + ",message:" + jsonResult.getString("message")
                 );
        throw new GuiguException(ResultCodeEnum.FAIL.getCode(), jsonResult.getString("message"));
    }

    JSONObject data = jsonResult.getJSONObject("data");
    String hosOrderId = data.getString("hosOrderId");
    Integer number = data.getInteger("number");
    String fetchTime = data.getString("fetchTime");
    String fetchAddress = data.getString("fetchAddress");
    orderInfo.setHosOrderId(hosOrderId);
    orderInfo.setNumber(number);
    orderInfo.setFetchTime(fetchTime);
    orderInfo.setFetchAddress(fetchAddress);
    baseMapper.insert(orderInfo);

    //使用这两个数据更新平台端的最新的排班数量
    Integer reservedNumber = data.getInteger("reservedNumber");
    Integer availableNumber = data.getInteger("availableNumber");

    //目的1:更新mongodb数据库中的排班数量
    //TODO 中间件:MQ 异步解耦

    //目的2:给就诊人发短信 TODO:MQ

    return orderInfo.getId(); //返回订单id
}

5、前端整合

5.1、api

创建api/orderInfo.js

import request from '@/utils/request'
export default {
  //生成订单
  submitOrder(scheduleId, patientId) {
    return request({
      url: `/front/order/orderInfo/auth/submitOrder/${scheduleId}/${patientId}`,
      method: 'post'
    })
  }
}

5.2、组件

在booking.vue组件中完善下单方法

<div class="v-button" @click="submitOrder()">{{orderText}}</div>

定义data

orderBtnDisabled: false, //防止重复提交
orderText: '确认挂号'

引入api

import orderInfoApi from '~/api/orderInfo'

完善方法

//确认挂号
submitOrder() {
    if (this.orderBtnDisabled) return
    this.orderBtnDisabled = true
    this.orderText = '预约挂号中...'
    orderInfoApi
        .submitOrder(this.scheduleId, this.patient.id)
        .then((response) => {
        window.location.href = '/order/show?orderId=' + response.data
    })
},

第02章-订单详情(作业)

1、后端接口

1.1、Controller

在FrontOrderInfoController中添加方法

@ApiOperation("根据订单id查询订单详情")
@ApiImplicitParam(name = "orderId",value = "订单id", required = true)
@GetMapping("/auth/getOrder/{orderId}")
public Result<OrderInfo> getOrder(@PathVariable Long orderId, HttpServletRequest request, HttpServletResponse response) {
    authContextHolder.checkAuth(request, response);
    OrderInfo orderInfo = orderInfoService.getOrderInfo(orderId);
    return Result.ok(orderInfo);
}

1.2、Service

接口:OrderInfoService

/**
     * 根据订单id获取订单详情
     * @param orderId
     * @return
     */
OrderInfo getOrderInfo(Long orderId);

实现:OrderInfoServiceImpl

@Override
public OrderInfo getOrderInfo(Long orderId) {
    OrderInfo orderInfo = baseMapper.selectById(orderId);
    return this.packOrderInfo(orderInfo);
}

辅助方法

/**
     * 封装订单数据
     * @param orderInfo
     * @return
     */
private OrderInfo packOrderInfo(OrderInfo orderInfo) {
    orderInfo.getParam().put(
        "orderStatusString", 
        OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus())
    );
    return orderInfo;
}

3、前端整合

3.1、api

在orderInfo.js添加方法

//订单详情
getOrder(orderId) {
    return request({
        url: `/front/order/orderInfo/auth/getOrder/${orderId}`,
        method: `get`
    })
},

2、页面渲染

资料:资料>订单微服务>order

pages/order/show.vue组件

第03章-订单列表(作业)

1、后端接口

1.1、Controller

在FrontOrderInfoController中添加方法

@ApiOperation("订单列表")
@GetMapping("/auth/list")
public Result<List<OrderInfo>> list(HttpServletRequest request, HttpServletResponse response) {
    Long userId = authContextHolder.checkAuth(request, response);
    List<OrderInfo> orderInfolist = orderInfoService.selectList(userId);
    return Result.ok(orderInfolist);
}

1.2、Service

接口:OrderInfoService

/**
     * 获取当前用户订单列表
     * @param userId
     * @return
     */
List<OrderInfo> selectList(Long userId);

实现:OrderInfoServiceImpl

@Override
public List<OrderInfo> selectList(Long userId) {

    LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(OrderInfo::getUserId, userId);
    List<OrderInfo> orderInfoList = baseMapper.selectList(queryWrapper);
    orderInfoList.stream().forEach(this::packOrderInfo);
    return orderInfoList;
}

2、前端整合

2.1、api

在orderInfo.js添加方法

//订单列表
getList() {
    return request({
        url: `/front/order/orderInfo/auth/list`,
        method: `get`
    })
},

2.2、页面渲

资料:资料>订单微服务>order

pages/order/index.vue组件

源码:https://gitee.com/dengyaojava/guigu-syt-parent

posted @ 2023-06-19 22:14  自律即自由-  阅读(192)  评论(0编辑  收藏  举报