更新了商品管理模块

This commit is contained in:
chen-xin-zhi 2024-12-09 15:31:39 +08:00
parent a451cc632e
commit 2cda8e26c6
31 changed files with 1410 additions and 153 deletions

View File

@ -177,6 +177,12 @@
<version>5.2.0</version>
</dependency>
<!-- 微信支付-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.10</version>
</dependency>
</dependencies>

View File

@ -0,0 +1,78 @@
package com.cultural.heritage.config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.util.IOUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.refund.RefundService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Data
@Slf4j
@Configuration
@Component("WxPayConfig")
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayConfig {
private String appId;
private String apiV3Key;
private String notifyUrl;
private String merchantId;
private String privateKeyPath;
private String merchantSerialNumber;
// RSA配置
private RSAAutoCertificateConfig RSAConfig;
// JSAPI支付
private JsapiServiceExtension jsapiServiceExtension;
// 退款
private RefundService refundService;
/**
* 初始化配置
*/
@Bean
public boolean initWxPayConfig() throws IOException {
this.RSAConfig = buildRSAAutoCertificateConfig();
this.jsapiServiceExtension = buildJsapiServiceExtension(RSAConfig);
this.refundService = buildRefundService(RSAConfig);
return true;
}
// 构建并使用自动更新平台证书的RSA配置一个商户号只能初始化一个配置否则会因为重复的下载任务报错
private RSAAutoCertificateConfig buildRSAAutoCertificateConfig() throws IOException {
// resource 目录下的文件转为 InputStream然后利用 IOUtil.toString(inputStream) 转化为密钥
String privateKey = IOUtil.toString(new ClassPathResource(privateKeyPath).getInputStream());
return new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
}
// 构建JSAPI支付
private JsapiServiceExtension buildJsapiServiceExtension(RSAAutoCertificateConfig config) {
return new JsapiServiceExtension.Builder().config(config).build();
}
// 构建退款
private RefundService buildRefundService(RSAAutoCertificateConfig config) {
return new RefundService.Builder().config(config).build();
}
}

View File

@ -1,6 +1,8 @@
package com.cultural.heritage.controller.book;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cultural.heritage.annotation.AuthCheck;
import com.cultural.heritage.common.BaseResponse;
@ -11,27 +13,35 @@ import com.cultural.heritage.constant.UserConstant;
import com.cultural.heritage.exception.BusinessException;
import com.cultural.heritage.exception.ThrowUtils;
import com.cultural.heritage.model.dto.CommonStringRequest;
import com.cultural.heritage.model.dto.book.BookingDateUpdateRequest;
import com.cultural.heritage.model.dto.book.BookingDateAddRequest;
import com.cultural.heritage.model.dto.timeinterval.TimeIntervalAddRequest;
import com.cultural.heritage.model.entity.BookingDate;
import com.cultural.heritage.model.entity.TimeInterval;
import com.cultural.heritage.model.vo.book.BookingDateVO;
import com.cultural.heritage.model.vo.timeinterval.TimeIntervalVO;
import com.cultural.heritage.service.book.BookingDateService;
import com.cultural.heritage.service.book.TimeIntervalService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/book")
@Slf4j
@Tag(name = "预约日期管理模块")
@Tag(name = "写真预约日期管理模块")
public class BookingDateController {
@ -39,75 +49,130 @@ public class BookingDateController {
private BookingDateService bookingDateService;
@Resource
private TimeIntervalService timeIntervalService;
/**
* (写真预约)初始化未来一天的预约日期
* Web端管理员添加写真预约日期和时间段
* @param bookingDateAddRequestList 预约日期列表
* @return 是否添加成功
*/
// @Scheduled(cron = "*/5 * * * * ?")
// 每天00:00-00:00调用
@Scheduled(cron = "0 0 0 * * ?")
@PostMapping("/add")
@Operation(summary = "Web端管理员添加写真预约日期和时间段", description = "参数:预约日期列表,权限:管理员(admin, boss)方法名addBookingDate")
@Transactional(rollbackFor = Exception.class)
@PostMapping("/init")
@Operation(summary = "(自动调用)初始化未来一天的预约日期", description = "参数权限自动调用方法名initCurrentBookingDate")
public void initCurrentBookingDate() {
// 初始化预约日期
List<BookingDate> bookingDateList = new ArrayList<>();
BookingDate bookingDateRent = bookingDateService.initBookingDate(BookConstant.RENT_CLOTHES, 3);
BookingDate bookingDateOwn = bookingDateService.initBookingDate(BookConstant.OWN_CLOTHES, 3);
bookingDateList.add(bookingDateRent);
bookingDateList.add(bookingDateOwn);
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> addBookingDate(@RequestBody List<BookingDateAddRequest> bookingDateAddRequestList) {
if (bookingDateAddRequestList == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 添加写真预约日期
List<BookingDate> bookingDateList = bookingDateAddRequestList.stream().map(bookingDateAddRequest -> {
BookingDate bookingDate = new BookingDate();
BeanUtils.copyProperties(bookingDateAddRequest, bookingDate);
bookingDateService.validBookingDate(bookingDate, false);
return bookingDate;
}).toList();
boolean result = bookingDateService.saveBatch(bookingDateList);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
// 添加写真预约时间段
List<TimeInterval> timeIntervals = new ArrayList<>();
for (int i = 0; i < bookingDateAddRequestList.size(); i++) {
BookingDate bookingDate = bookingDateList.get(i);
Long dateId = bookingDate.getId();
BookingDateAddRequest bookingDateAddRequest = bookingDateAddRequestList.get(i);
List<TimeIntervalAddRequest> timeIntervalAddRequestList = bookingDateAddRequest.getTimeIntervalAddRequestList();
List<TimeInterval> timeIntervalList = timeIntervalAddRequestList.stream().map(timeIntervalAddRequest -> {
TimeInterval timeInterval = new TimeInterval();
BeanUtils.copyProperties(timeIntervalAddRequest, timeInterval);
timeInterval.setBookingDateId(dateId);
timeIntervalService.validTimeInterval(timeInterval, false);
return timeInterval;
}).toList();
timeIntervals.addAll(timeIntervalList);
}
boolean success = timeIntervalService.saveBatch(timeIntervals);
ThrowUtils.throwIf(!success, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 根据预约类别查询未来四天(包括今天)的预约日期
* Web端管理员根据预约类型查询预约时间表
* @param commonStringRequest 预约类型
* @return 当前预约类型未来四天的预约日期列表
* @return 预约时间
*/
@PostMapping("/list")
@Operation(summary = "(小程序端)根据预约类别查询未来四天(包括今天)的预约日期", description = "参数:预约类型, 权限所有人方法名listBookingDateByType")
@PostMapping("/list/type")
@Operation(summary = "Web端管理员根据预约类型查询预约时间表", description = "参数:预约类型,权限:管理员(admin, boss)方法名listBookingDateByType")
@Transactional(rollbackFor = Exception.class)
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<List<BookingDateVO>> listBookingDateByType(@RequestBody CommonStringRequest commonStringRequest) {
if (commonStringRequest == null || StringUtils.isBlank(commonStringRequest.getType())) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String type = commonStringRequest.getType();
if (!type.equals(BookConstant.RENT_CLOTHES) && !type.equals(BookConstant.OWN_CLOTHES)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "类型参数错误");
}
QueryWrapper<BookingDate> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", type);
List<BookingDate> bookingDateList = bookingDateService.list(queryWrapper);
// 封装预约日期VO
List<BookingDateVO> bookingDateVOS = bookingDateList.stream().map(bookingDate -> {
List<TimeInterval> timeIntervalList = timeIntervalService.list();
// 存储写真预约时间段的map集合(:预约日期id, :预约时间段列表)
Map<Long, List<TimeIntervalVO>> timeMap = new HashMap<>();
// 处理预约时间段将预约时间段存入map集合
for (TimeInterval timeInterval : timeIntervalList) {
TimeIntervalVO timeIntervalVO = new TimeIntervalVO();
BeanUtils.copyProperties(timeInterval, timeIntervalVO);
Long bookingDateId = timeInterval.getBookingDateId();
List<TimeIntervalVO> timeIntervalVOS = timeMap.get(bookingDateId);
if (timeIntervalVOS == null) {
timeIntervalVOS = new ArrayList<>();
}
timeIntervalVOS.add(timeIntervalVO);
timeMap.put(bookingDateId, timeIntervalVOS);
}
// 处理预约日期填充当前预约日期对应的预约时间段
List<BookingDateVO> bookingDateVOList = new ArrayList<>();
for (BookingDate bookingDate : bookingDateList) {
BookingDateVO bookingDateVO = new BookingDateVO();
BeanUtils.copyProperties(bookingDate, bookingDateVO);
return bookingDateVO;
}).toList();
return ResultUtils.success(bookingDateVOS);
}
/**
* 更新当天的预约情况
* @param bookingDateUpdateRequest 预约日期更新请求体
* @return 是否更新成功
*/
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
@Operation(summary = "Web端管理员更新当天的预约情况", description = "参数:预约日期更新请求体, 权限:管理员(admin, boss)方法名updateBookingDate")
public BaseResponse<Boolean> updateBookingDate(@RequestBody BookingDateUpdateRequest bookingDateUpdateRequest) {
if (bookingDateUpdateRequest == null || bookingDateUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
Long dateId = bookingDate.getId();
List<TimeIntervalVO> timeIntervalVOS = timeMap.get(dateId);
bookingDateVO.setTimeIntervalVOList(timeIntervalVOS);
bookingDateVOList.add(bookingDateVO);
}
BookingDate bookingDate = new BookingDate();
BeanUtils.copyProperties(bookingDateUpdateRequest, bookingDate);
boolean result = bookingDateService.updateById(bookingDate);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
// 将预约日期从小到大排序
bookingDateVOList.sort((book1, book2) -> {
DateTime date1 = DateUtil.parse(book1.getSpecificDate(), "yyyy-MM-dd");
DateTime date2 = DateUtil.parse(book2.getSpecificDate(), "yyyy-MM-dd");
return date1.compareTo(date2);
});
return ResultUtils.success(bookingDateVOList);
}
// @PostMapping("/update")
// @Operation(summary = "Web端管理员根据预约类型更新预约时间表", description = "参数:预约类型,权限:管理员(admin, boss)方法名updateBookingDate")
// @Transactional(rollbackFor = Exception.class)
// @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
// public BaseResponse<Boolean> updateBookingDate(@RequestBody CommonStringRequest commonStringRequest) {
// if (commonStringRequest == null || StringUtils.isBlank(commonStringRequest.getType())) {
// throw new BusinessException(ErrorCode.PARAMS_ERROR);
// }
// String type = commonStringRequest.getType();
// if (!type.equals(BookConstant.RENT_CLOTHES) && !type.equals(BookConstant.OWN_CLOTHES)) {
// throw new BusinessException(ErrorCode.PARAMS_ERROR, "类型参数错误");
// }
// QueryWrapper<BookingDate> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("type", type);
// return null;
// }
}

View File

@ -0,0 +1,210 @@
package com.cultural.heritage.controller.good;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.cultural.heritage.annotation.AuthCheck;
import com.cultural.heritage.common.BaseResponse;
import com.cultural.heritage.common.ErrorCode;
import com.cultural.heritage.common.ResultUtils;
import com.cultural.heritage.constant.UserConstant;
import com.cultural.heritage.exception.BusinessException;
import com.cultural.heritage.exception.ThrowUtils;
import com.cultural.heritage.model.dto.CommonRequest;
import com.cultural.heritage.model.dto.appointment.single.AppointmentDateSingleAddRequest;
import com.cultural.heritage.model.dto.timeperiod.TimePeriodAddRequest;
import com.cultural.heritage.model.dto.timeperiod.single.TimePeriodSingleAddRequest;
import com.cultural.heritage.model.dto.timeperiod.single.TimePeriodSingleUpdateRequest;
import com.cultural.heritage.model.entity.AppointmentDate;
import com.cultural.heritage.model.entity.TimePeriod;
import com.cultural.heritage.service.good.AppointmentDateService;
import com.cultural.heritage.service.good.TimePeriodService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
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 java.util.List;
@RestController
@RequestMapping("/appointmentDate")
@Slf4j
@Tag(name = "服务类商品预约日期模块")
public class AppointmentDateController {
@Resource
private AppointmentDateService appointmentDateService;
@Resource
private TimePeriodService timePeriodService;
/**
* Web端管理员根据id删除预约日期
* @param commonRequest 预约日期id
* @return 是否删除成功
*/
@PostMapping("/del/id")
@Operation(summary = "Web端管理员根据id删除预约日期", description = "参数预约日期id权限管理员(admin, boss)方法名delAppointmentDateById")
@Transactional(rollbackFor = Exception.class)
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> delAppointmentDateById(@RequestBody CommonRequest commonRequest) {
if (commonRequest == null || commonRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Long id = commonRequest.getId();
// 删除这个预约日期关联的所有时间段
QueryWrapper<TimePeriod> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("appointmentDateId", id);
boolean remove = timePeriodService.remove(queryWrapper);
ThrowUtils.throwIf(!remove, ErrorCode.OPERATION_ERROR, "预约时间段删除失败");
// 删除这个预约日期
boolean success = appointmentDateService.removeById(id);
ThrowUtils.throwIf(!success, ErrorCode.OPERATION_ERROR, "预约日期删除失败");
return ResultUtils.success(true);
}
/**
* Web端管理员根据id删除预约时间段
* @param commonRequest 预约时间段id
* @return 是否删除成功
*/
@PostMapping("/del/time/id")
@Operation(summary = "Web端管理员根据id删除预约时间段", description = "参数预约日期段id权限管理员(admin, boss)方法名delTimePeriodById")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> delTimePeriodById(@RequestBody CommonRequest commonRequest) {
if (commonRequest == null || commonRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Long id = commonRequest.getId();
boolean remove = timePeriodService.removeById(id);
ThrowUtils.throwIf(!remove, ErrorCode.OPERATION_ERROR, "预约时间段删除失败");
return ResultUtils.success(true);
}
/**
* Web端管理员根据id修改预约日期的状态
* @param commonRequest 预约日期id
* @return 是否更新成功
*/
@PostMapping("/update/status")
@Operation(summary = "Web端管理员根据id修改预约日期的状态", description = "参数预约日期id权限管理员(admin, boss)方法名updateTimePeriodStatusById")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateTimePeriodStatusById(@RequestBody CommonRequest commonRequest) {
if (commonRequest == null || commonRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Long id = commonRequest.getId();
AppointmentDate appointmentDate = appointmentDateService.getById(id);
ThrowUtils.throwIf(appointmentDate == null, ErrorCode.OPERATION_ERROR, "预约日期不存在");
Integer status = appointmentDate.getIsAvailable() == 0 ? 1 : 0;
UpdateWrapper<AppointmentDate> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", id);
updateWrapper.set("isAvailable", status);
boolean update = appointmentDateService.update(updateWrapper);
ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "预约日期状态修改失败");
return ResultUtils.success(true);
}
/**
* Web端管理员根据id修改预约时间段的人数
* @param timePeriodSingleUpdateRequest 预约日期id
* @return 是否更新成功
*/
@PostMapping("/update/time")
@Operation(summary = "Web端管理员根据id修改预约时间段的人数", description = "参数预约日期id权限管理员(admin, boss)方法名updateTimePeriodPersonNumberById")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateTimePeriodPersonNumberById(@RequestBody TimePeriodSingleUpdateRequest timePeriodSingleUpdateRequest) {
if (timePeriodSingleUpdateRequest == null || timePeriodSingleUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 根据id获取当前的预约日期
Long id = timePeriodSingleUpdateRequest.getId();
TimePeriod time = timePeriodService.getById(id);
// 封装更新后的预约时间段
TimePeriod timePeriod = new TimePeriod();
BeanUtils.copyProperties(timePeriodSingleUpdateRequest, timePeriod);
timePeriod.setId(time.getId());
timePeriod.setTimeSlot(time.getTimeSlot());
boolean update = timePeriodService.updateById(timePeriod);
ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "预约时间段人数修改失败");
return ResultUtils.success(true);
}
/**
* Web端管理员添加预约时间段
* @param timePeriodSingleAddRequest 预约时间段添加请求体
* @return 是否添加成功
*/
@PostMapping("/add/time")
@Operation(summary = "Web端管理员添加预约时间段", description = "参数:预约时间段添加请求体,权限:管理员(admin, boss)方法名addAppointmentDate")
@Transactional(rollbackFor = Exception.class)
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> addTimePeriod(@RequestBody TimePeriodSingleAddRequest timePeriodSingleAddRequest) {
if (timePeriodSingleAddRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
TimePeriod timePeriod = new TimePeriod();
BeanUtils.copyProperties(timePeriodSingleAddRequest, timePeriod);
boolean save = timePeriodService.save(timePeriod);
ThrowUtils.throwIf(!save, ErrorCode.OPERATION_ERROR, "预约时间段添加失败");
return ResultUtils.success(true);
}
/**
* Web端管理员添加预约日期
* @param appointmentDateSingleAddRequest 预约日期添加请求体
* @return 是否添加成功
*/
@PostMapping("/add")
@Operation(summary = "Web端管理员添加预约日期", description = "参数:预约日期添加请求体,权限:管理员(admin, boss)方法名addAppointmentDate")
@Transactional(rollbackFor = Exception.class)
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> addAppointmentDate(@RequestBody AppointmentDateSingleAddRequest appointmentDateSingleAddRequest) {
if (appointmentDateSingleAddRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 添加当前商品的预约日期
AppointmentDate appointmentDate = new AppointmentDate();
BeanUtils.copyProperties(appointmentDateSingleAddRequest, appointmentDate);
boolean save = appointmentDateService.save(appointmentDate);
ThrowUtils.throwIf(!save, ErrorCode.OPERATION_ERROR, "预约日期添加失败");
// 添加当前预约日期的预约时间段
Long appointmentDateId = appointmentDate.getId();
List<TimePeriodAddRequest> timePeriodAddRequestList = appointmentDateSingleAddRequest.getTimePeriodAddRequestList();
List<TimePeriod> timePeriodList = timePeriodAddRequestList.stream().map(timePeriodAddRequest -> {
TimePeriod timePeriod = new TimePeriod();
BeanUtils.copyProperties(timePeriodAddRequest, timePeriod);
timePeriod.setAppointmentDateId(appointmentDateId);
return timePeriod;
}).toList();
boolean result = timePeriodService.saveBatch(timePeriodList);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "批量添加预约时间段失败");
return ResultUtils.success(true);
}
}

View File

@ -1,6 +1,8 @@
package com.cultural.heritage.controller.good;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -14,7 +16,6 @@ import com.cultural.heritage.exception.ThrowUtils;
import com.cultural.heritage.model.dto.CommonDelBatchRequest;
import com.cultural.heritage.model.dto.CommonRequest;
import com.cultural.heritage.model.dto.appointment.AppointmentDateAddRequest;
import com.cultural.heritage.model.dto.appointment.AppointmentDateUpdateRequest;
import com.cultural.heritage.model.dto.good.GoodAddRequest;
import com.cultural.heritage.model.dto.good.GoodQueryRequest;
import com.cultural.heritage.model.dto.good.GoodUpdateRequest;
@ -291,7 +292,7 @@ public class GoodController {
public BaseResponse<Page<GoodPageVO>> listGoodByPage(@RequestBody GoodQueryRequest goodQueryRequest) {
long current = goodQueryRequest.getCurrent();
long pageSize = goodQueryRequest.getPageSize();
QueryWrapper<Good> goodQueryWrapper = goodService.getGoodQueryWrapper(goodQueryRequest, true);
QueryWrapper<Good> goodQueryWrapper = goodService.getGoodQueryWrapper(goodQueryRequest, false);
Page<Good> page = goodService.page(new Page<>(current, pageSize), goodQueryWrapper);
List<Good> records = page.getRecords();
List<GoodPageVO> goodPageVOS = records.stream().map(good -> {
@ -409,6 +410,13 @@ public class GoodController {
appointmentDateVOList.add(appointmentDateVO);
}
// 将预约日期从小到大排序
appointmentDateVOList.sort((app1, app2) -> {
DateTime date1 = DateUtil.parse(app1.getSpecificDate(), "yyyy-MM-dd");
DateTime date2 = DateUtil.parse(app2.getSpecificDate(), "yyyy-MM-dd");
return date1.compareTo(date2);
});
List<ServiceGoodVO> serviceGoodVOList = new ArrayList<>();
for (Good good : records) {
ServiceGoodVO serviceGoodVO = new ServiceGoodVO();
@ -481,6 +489,14 @@ public class GoodController {
appointmentDateVO.setTimePeriodVOList(timePeriodVOList);
appointmentDateVOList.add(appointmentDateVO);
}
// 将预约日期从小到大排序
appointmentDateVOList.sort((app1, app2) -> {
DateTime date1 = DateUtil.parse(app1.getSpecificDate(), "yyyy-MM-dd");
DateTime date2 = DateUtil.parse(app2.getSpecificDate(), "yyyy-MM-dd");
return date1.compareTo(date2);
});
serviceGoodVO.setAppointmentDateVOList(appointmentDateVOList);
return ResultUtils.success(serviceGoodVO);
}
@ -517,6 +533,7 @@ public class GoodController {
*/
@PostMapping("/service/update")
@Operation(summary = "Web端管理员更新服务类商品", description = "参数:服务类商品更新请求体,权限:管理员(admin, boss)方法名updateServiceGoodById")
@Transactional(rollbackFor = Exception.class)
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateServiceGoodById(@RequestBody ServiceGoodUpdateRequest serviceGoodUpdateRequest) {
if (serviceGoodUpdateRequest == null || serviceGoodUpdateRequest.getId() <= 0) {
@ -534,17 +551,74 @@ public class GoodController {
boolean result = goodService.updateById(good);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "商品不存在");
List<AppointmentDateUpdateRequest> appointmentDateUpdateRequestList = serviceGoodUpdateRequest.getAppointmentDateUpdateRequestList();
List<AppointmentDate> appointmentDateList = appointmentDateUpdateRequestList.stream().map(appointmentDateUpdateRequest -> {
// 删除当前商品关联的所有日期和时间段
CommonRequest commonRequest = new CommonRequest();
commonRequest.setId(good.getId());
deleteServiceGoodAppointment(commonRequest);
// 为当前商品重新添加预约日期和时间段
ServiceGoodAddRequest serviceGoodAddRequest = new ServiceGoodAddRequest();
BeanUtils.copyProperties(serviceGoodUpdateRequest, serviceGoodAddRequest);
dealServiceGoodAppointmentDate(serviceGoodAddRequest, good);
return ResultUtils.success(true);
}
private void deleteServiceGoodAppointment(CommonRequest commonRequest) {
if (commonRequest == null || commonRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Long id = commonRequest.getId();
//删除预约时间段表中与该商品关联的记录
QueryWrapper<AppointmentDate> dateQueryWrapper = new QueryWrapper<>();
dateQueryWrapper.eq("goodId", id);
List<AppointmentDate> appointmentDateList = appointmentDateService.list(dateQueryWrapper);
List<Long> ids = appointmentDateList.stream().map(AppointmentDate::getId).toList();
QueryWrapper<TimePeriod> timePeriodQueryWrapper = new QueryWrapper<>();
timePeriodQueryWrapper.in("appointmentDateId", ids);
boolean remove = timePeriodService.remove(timePeriodQueryWrapper);
ThrowUtils.throwIf(!remove, ErrorCode.OPERATION_ERROR, "服务类商品预约时间段删除失败");
// 删除预约日期表中与该商品关联的记录
boolean isSuccess = appointmentDateService.remove(dateQueryWrapper);
ThrowUtils.throwIf(!isSuccess, ErrorCode.OPERATION_ERROR, "服务类商品预约日期删除失败");
}
private void dealServiceGoodAppointmentDate(ServiceGoodAddRequest serviceGoodAddRequest, Good good) {
// 添加当前商品的预约日期
List<AppointmentDateAddRequest> appointmentDateAddRequestList = serviceGoodAddRequest.getAppointmentDateAddRequestList();
List<AppointmentDate> appointmentDateList = appointmentDateAddRequestList.stream().map(appointmentDateAddRequest -> {
AppointmentDate appointmentDate = new AppointmentDate();
BeanUtils.copyProperties(appointmentDateUpdateRequest, appointmentDate);
BeanUtils.copyProperties(appointmentDateAddRequest, appointmentDate);
appointmentDate.setGoodId(good.getId());
// 校验
appointmentDateService.validAppointmentDate(appointmentDate, false);
return appointmentDate;
}).toList();
boolean isSaveBatch = appointmentDateService.saveBatch(appointmentDateList);
ThrowUtils.throwIf(!isSaveBatch, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
// 添加当前商品的预约时间段
List<TimePeriod> timePeriods = new ArrayList<>();
for (int i = 0; i < appointmentDateAddRequestList.size(); i++) {
AppointmentDate appointmentDate = appointmentDateList.get(i);
AppointmentDateAddRequest appointmentDateAddRequest = appointmentDateAddRequestList.get(i);
Long appointmentDateId = appointmentDate.getId();
List<TimePeriodAddRequest> timePeriodAddRequestList = appointmentDateAddRequest.getTimePeriodAddRequestList();
List<TimePeriod> timePeriodList = timePeriodAddRequestList.stream().map(timePeriodAddRequest -> {
TimePeriod timePeriod = new TimePeriod();
BeanUtils.copyProperties(timePeriodAddRequest, timePeriod);
timePeriod.setAppointmentDateId(appointmentDateId);
// 校验
timePeriodService.validTimePeriod(timePeriod, false);
return timePeriod;
}).toList();
timePeriods.addAll(timePeriodList);
}
boolean result = timePeriodService.saveBatch(timePeriods);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
}

View File

@ -0,0 +1,120 @@
package com.cultural.heritage.controller.wx;
import com.cultural.heritage.annotation.AuthCheck;
import com.cultural.heritage.common.BaseResponse;
import com.cultural.heritage.common.ErrorCode;
import com.cultural.heritage.common.ResultUtils;
import com.cultural.heritage.constant.UserConstant;
import com.cultural.heritage.exception.BusinessException;
import com.cultural.heritage.exception.ThrowUtils;
import com.cultural.heritage.model.dto.CommonRequest;
import com.cultural.heritage.model.entity.Order;
import com.cultural.heritage.model.entity.User;
import com.cultural.heritage.service.order.OrderService;
import com.cultural.heritage.service.user.UserService;
import com.cultural.heritage.service.wxpay.WeChatService;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
/**
* 微信小程序相关接口
**/
@Slf4j
@RestController
@Tag(name = "微信支付接口")
@RequestMapping("/wechat")
public class WeChatPayController {
@Resource
private UserService userService;
@Resource
private OrderService ordersService;
@Resource
private WeChatService weChatService;
/**
* JSAPI 下单
*/
@PostMapping("/payment/create")
public BaseResponse<PrepayWithRequestPaymentResponse> createPayment(@RequestBody CommonRequest commonRequest, HttpServletRequest request) {
User loginUser = userService.getLoginUser(request);
String miniOpenId = loginUser.getMiniOpenId();
ThrowUtils.throwIf(miniOpenId == null, ErrorCode.NOT_FOUND_ERROR, "不是小程序用户");
Long orderId = commonRequest.getId();
Order order = ordersService.getById(orderId);
ThrowUtils.throwIf(order == null, ErrorCode.NOT_FOUND_ERROR, "订单不存在");
// ThrowUtils.throwIf(order.getState() != 0, ErrorCode.OPERATION_ERROR, "订单状态错误");
if (!loginUser.getId().equals(order.getUserId())) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "你不是该订单用户!");
}
PrepayWithRequestPaymentResponse response = weChatService.createPayment(String.valueOf(orderId), miniOpenId, order.getTotalAmount());
return ResultUtils.success(response);
}
/**
* JSAPI 下单回调
*/
@PostMapping("/payment/callback")
@Transactional(rollbackFor = Exception.class)
public synchronized BaseResponse<Boolean> callbackPayment(HttpServletRequest request) throws IOException {
// 获取下单信息
Transaction transaction = weChatService.getTransactionInfo(request);
System.out.println("下单信息:" + transaction);
// 支付回调
boolean result = weChatService.paymentCallback(transaction);
ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "微信支付回调失败");
return ResultUtils.success(true);
}
/**
* 退款仅管理员和商家
*/
@PostMapping("/refund/create")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Refund> createRefund(@RequestBody CommonRequest commonRequest) {
Long orderId = commonRequest.getId();
Order order = ordersService.getById(orderId);
ThrowUtils.throwIf(order == null, ErrorCode.NOT_FOUND_ERROR, "订单不存在");
Refund refund = weChatService.refundPayment(String.valueOf(orderId), order.getTotalAmount());
return ResultUtils.success(refund);
}
/**
* 退款回调
*/
@PostMapping("/refund/callback")
public BaseResponse<Boolean> callbackRefund(HttpServletRequest request) {
// 获取退款信息
RefundNotification refundNotification = weChatService.getRefundInfo(request);
// 退款回调
boolean result = weChatService.refundCallback(refundNotification);
ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "退款回调失败");
return ResultUtils.success(true);
}
/**
* 发送订阅消息
*/
@GetMapping("/refund/callback")
public BaseResponse<Boolean> testSendMessage() {
String miniOpenId = "o0o_B5CMLFiOs96dJZwtkyHcJzcM";
String templateId = "MK13FfX0XxsPV6m1vi6J8_8Bf7JT8rsayFs9q3f4FW4";
boolean subscribeMessage = weChatService.sendSubscribeMessage(miniOpenId, templateId);
return ResultUtils.success(subscribeMessage);
}
}

View File

@ -0,0 +1,7 @@
package com.cultural.heritage.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cultural.heritage.model.entity.TimeInterval;
public interface TimeIntervalMapper extends BaseMapper<TimeInterval> {
}

View File

@ -0,0 +1,44 @@
package com.cultural.heritage.model.dto.appointment.single;
import com.cultural.heritage.model.dto.timeperiod.TimePeriodAddRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
@Data
@Schema(description = "预约日期请求体", requiredProperties = {
"specificDate", "isAvailable", "timePeriodAddRequestList"
})
public class AppointmentDateSingleAddRequest implements Serializable {
/**
* 商品id
*/
@Schema(description = "商品id", example = "2")
private Long goodId;
/**
* 具体预约时间
*/
@Schema(description = "具体预约时间", example = "2024-12-04")
private String specificDate;
/**
* 是否可预约
*/
@Schema(description = "是否可预约", example = "1")
private Integer isAvailable;
/**
* 预约时间段列表
*/
private List<TimePeriodAddRequest> timePeriodAddRequestList;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -1,13 +1,15 @@
package com.cultural.heritage.model.dto.book;
import com.cultural.heritage.model.dto.timeinterval.TimeIntervalAddRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
@Data
@Schema(description = "预约日期添加请求体", requiredProperties = {"specificDate", "timeSlot", "isAvailable", "maxNumber", "type"})
@Schema(description = "预约日期添加请求体", requiredProperties = {"specificDate", "isAvailable", "type"})
public class BookingDateAddRequest implements Serializable {
/**
@ -17,13 +19,6 @@ public class BookingDateAddRequest implements Serializable {
private String specificDate;
/**
* 时间段
*/
@Schema(description = "时间段", example = "8:00-10:00;12:00-14:00")
private String timeSlot;
/**
* 是否可预约
*/
@ -31,13 +26,6 @@ public class BookingDateAddRequest implements Serializable {
private Integer isAvailable;
/**
* 各个时间段的最大预约人数(4,6..)
*/
@Schema(description = "各个时间段的最大预约人数(4,6..)", example = "4,5,6")
private String maxNumber;
/**
* 预约类别
*/
@ -45,6 +33,13 @@ public class BookingDateAddRequest implements Serializable {
private String type;
/**
* 预约时间段列表
*/
@Schema(description = "预约时间段列表")
private List<TimeIntervalAddRequest> timeIntervalAddRequestList;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -7,7 +7,7 @@ import java.io.Serial;
import java.io.Serializable;
@Data
@Schema(description = "预约日期更新请求体", requiredProperties = {"id", "specificDate", "timeSlot", "isAvailable", "maxNumber","type"})
@Schema(description = "预约日期更新请求体", requiredProperties = {"id", "specificDate", "isAvailable","type"})
public class BookingDateUpdateRequest implements Serializable {
@ -25,13 +25,6 @@ public class BookingDateUpdateRequest implements Serializable {
private String specificDate;
/**
* 时间段
*/
@Schema(description = "时间段", example = "8:00-10:00;12:00-14:00")
private String timeSlot;
/**
* 是否可预约
*/
@ -39,13 +32,6 @@ public class BookingDateUpdateRequest implements Serializable {
private Integer isAvailable;
/**
* 各个时间段的最大预约人数(4,6..)
*/
@Schema(description = "各个时间段的最大预约人数(4,6..)", example = "4,5,6")
private String maxNumber;
/**
* 预约类别
*/

View File

@ -1,6 +1,6 @@
package com.cultural.heritage.model.dto.good.service;
import com.cultural.heritage.model.dto.appointment.AppointmentDateUpdateRequest;
import com.cultural.heritage.model.dto.appointment.AppointmentDateAddRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -67,7 +67,7 @@ public class ServiceGoodUpdateRequest implements Serializable {
* 未来几天的预约时间表
*/
@Schema(description = "未来的预约时间表")
private List<AppointmentDateUpdateRequest> appointmentDateUpdateRequestList;
private List<AppointmentDateAddRequest> appointmentDateAddRequestList;
@Serial

View File

@ -0,0 +1,40 @@
package com.cultural.heritage.model.dto.timeinterval;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
@Schema(description = "写真预约时间段添加请求体", requiredProperties = {
"timeSlot", "minNumber", "maxNumber"})
public class TimeIntervalAddRequest implements Serializable {
/**
* 时间段
*/
@Schema(description = "时间段", example = "08:00-10:00")
private String timeSlot;
/**
* 最小预约人数
*/
@Schema(description = "最小预约人数", example = "3")
private Integer minNumber;
/**
* 最大预约人数
*/
@Schema(description = "最大预约人数", example = "10")
private Integer maxNumber;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -17,6 +17,7 @@ public class TimePeriodUpdateRequest implements Serializable {
@Schema(description = "预约时间段id(id > 0)", example = "10")
private Long id;
/**
* 时间段
*/
@ -39,6 +40,13 @@ public class TimePeriodUpdateRequest implements Serializable {
/**
* 预约日期id
*/
@Schema(description = "预约日期id", example = "12")
private Long appointmentDateId;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,46 @@
package com.cultural.heritage.model.dto.timeperiod.single;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
@Schema(description = "预约时间段添加请求体", requiredProperties = {
"timeSlot", "minNumber", "maxNumber"
})
public class TimePeriodSingleAddRequest implements Serializable {
/**
* 时间段
*/
@Schema(description = "时间段", example = "08:00-10:00")
private String timeSlot;
/**
* 最小预约人数
*/
@Schema(description = "最小预约人数", example = "3")
private Integer minNumber;
/**
* 最大预约人数
*/
@Schema(description = "最大预约人数", example = "10")
private Integer maxNumber;
/**
* 预约日期id
*/
@Schema(description = "预约日期id", example = "12")
private Long appointmentDateId;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,36 @@
package com.cultural.heritage.model.dto.timeperiod.single;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
@Schema(description = "预约时间段更新请求体", requiredProperties = {"id", "minNumber", "maxNumber"})
public class TimePeriodSingleUpdateRequest implements Serializable {
/**
* 预约时间段id
*/
@Schema(description = "预约时间段id(id > 0)", example = "10")
private Long id;
/**
* 最小预约人数
*/
@Schema(description = "最小预约人数", example = "3")
private Integer minNumber;
/**
* 最大预约人数
*/
@Schema(description = "最大预约人数", example = "10")
private Integer maxNumber;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -31,24 +31,12 @@ public class BookingDate implements Serializable {
private String specificDate;
/**
* 时间段
*/
private String timeSlot;
/**
* 是否可预约
*/
private Integer isAvailable;
/**
* 各个时间段的最大预约人数(4,6..)
*/
private String maxNumber;
/**
* 预约类别
*/

View File

@ -0,0 +1,67 @@
package com.cultural.heritage.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 写真预约时间段表
* @TableName time_interval
*/
@Data
@TableName("time_interval")
public class TimeInterval implements Serializable {
/**
* 预约时间段id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 时间段
*/
private String timeSlot;
/**
* 最小预约人数
*/
private Integer minNumber;
/**
* 最大预约人数
*/
private Integer maxNumber;
/**
* 预约日期id
*/
private Long bookingDateId;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 是否删除
*/
private Integer isDelete;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -1,9 +1,11 @@
package com.cultural.heritage.model.vo.book;
import com.cultural.heritage.model.vo.timeinterval.TimeIntervalVO;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
@Data
public class BookingDateVO implements Serializable {
@ -21,30 +23,23 @@ public class BookingDateVO implements Serializable {
private String specificDate;
/**
* 时间段
*/
private String timeSlot;
/**
* 是否可预约
*/
private Integer isAvailable;
/**
* 各个时间段的最大预约人数(4,6..)
*/
private String maxNumber;
/**
* 预约类别
*/
private String type;
/**
* 预约时间段
*/
private List<TimeIntervalVO> timeIntervalVOList;
@Serial

View File

@ -0,0 +1,36 @@
package com.cultural.heritage.model.vo.timeinterval;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class TimeIntervalVO implements Serializable {
/**
* 预约时间段id
*/
private Long id;
/**
* 时间段
*/
private String timeSlot;
/**
* 最小预约人数
*/
private Integer minNumber;
/**
* 最大预约人数
*/
private Integer maxNumber;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -7,7 +7,7 @@ public interface BookingDateService extends IService<BookingDate> {
/**
* 初始化未来一天的预约日期
* 校验
*/
BookingDate initBookingDate(String type, int offset);
void validBookingDate(BookingDate bookingDate, boolean update);
}

View File

@ -0,0 +1,13 @@
package com.cultural.heritage.service.book;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cultural.heritage.model.entity.TimeInterval;
public interface TimeIntervalService extends IService<TimeInterval> {
/**
* 校验
*/
void validTimeInterval(TimeInterval timeInterval, boolean update);
}

View File

@ -1,28 +1,37 @@
package com.cultural.heritage.service.book.impl;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cultural.heritage.common.ErrorCode;
import com.cultural.heritage.exception.BusinessException;
import com.cultural.heritage.mapper.BookingDateMapper;
import com.cultural.heritage.model.entity.BookingDate;
import com.cultural.heritage.service.book.BookingDateService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@Service
public class BookingDateServiceImpl extends ServiceImpl<BookingDateMapper, BookingDate> implements BookingDateService {
/**
* 初始化未来一天的预约日期
*/
@Override
public BookingDate initBookingDate(String type, int offset) {
String currentDate = DateUtil.formatDate(DateUtil.offsetDay(DateUtil.date(), offset));
BookingDate bookingDate = new BookingDate();
bookingDate.setSpecificDate(currentDate);
bookingDate.setTimeSlot("00:00-00:00");
bookingDate.setIsAvailable(0);
bookingDate.setMaxNumber("0");
bookingDate.setType(type);
return bookingDate;
public void validBookingDate(BookingDate bookingDate, boolean update) {
Long dateId = bookingDate.getId();
String specificDate = bookingDate.getSpecificDate();
Integer isAvailable = bookingDate.getIsAvailable();
String type = bookingDate.getType();
if (update) {
if (dateId == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "id参数错误");
}
}
if (ObjectUtils.isEmpty(isAvailable) || isAvailable != 1 && isAvailable != 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "是否可预约参数错误");
}
if (StringUtils.isAnyBlank(specificDate, type)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "存在字符串类型参数为空");
}
}
}

View File

@ -0,0 +1,44 @@
package com.cultural.heritage.service.book.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cultural.heritage.common.ErrorCode;
import com.cultural.heritage.exception.BusinessException;
import com.cultural.heritage.mapper.TimeIntervalMapper;
import com.cultural.heritage.model.entity.TimeInterval;
import com.cultural.heritage.service.book.TimeIntervalService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@Service
public class TimeIntervalServiceImpl extends ServiceImpl<TimeIntervalMapper, TimeInterval> implements TimeIntervalService {
/**
* 校验
*/
@Override
public void validTimeInterval(TimeInterval timeInterval, boolean update) {
Long id = timeInterval.getId();
Integer minNumber = timeInterval.getMinNumber();
Integer maxNumber = timeInterval.getMaxNumber();
String timeSlot = timeInterval.getTimeSlot();
Long bookingDateId = timeInterval.getBookingDateId();
if (update) {
if (id == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "预约日期id参数错误");
}
}
if (ObjectUtils.anyNull(minNumber, maxNumber, bookingDateId)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "存在参数为空");
}
if (minNumber > maxNumber) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "最小预约人数不能超过最大预约人数");
}
if (StringUtils.isBlank(timeSlot)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "时间段参数为空");
}
}
}

View File

@ -0,0 +1,53 @@
package com.cultural.heritage.service.wxpay;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
/**
* @author 玄德
*/
public interface WeChatService {
/**
* 微信支付
*/
PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount);
/**
* 获取支付回调信息
*/
Transaction getTransactionInfo(HttpServletRequest request);
/**
* 支付回调
*/
boolean paymentCallback(Transaction transaction) throws IOException;
/**
* 退款申请
*/
Refund refundPayment(String orderId, BigDecimal amount);
/**
* 获取退款回调信息
*/
RefundNotification getRefundInfo(HttpServletRequest request);
/**
* 退款回调
*/
boolean refundCallback(RefundNotification refundNotification);
/**
* 发送订阅模板消息
*/
boolean sendSubscribeMessage(String openid, String templateId);
}

View File

@ -0,0 +1,265 @@
package com.cultural.heritage.service.wxpay.impl;
import cn.binarywang.wx.miniapp.api.WxMaMsgService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.hutool.core.util.RandomUtil;
import com.cultural.heritage.common.ErrorCode;
import com.cultural.heritage.config.WxOpenConfig;
import com.cultural.heritage.config.WxPayConfig;
import com.cultural.heritage.exception.BusinessException;
import com.cultural.heritage.model.entity.Order;
import com.cultural.heritage.service.order.OrderService;
import com.cultural.heritage.service.wxpay.WeChatService;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
/**
* @author 玄德
*/
@Slf4j
@Service
public class WeChatServiceImpl implements WeChatService {
@Resource
private WxPayConfig wxPayConfig;
@Resource
private WxOpenConfig wxOpenConfig;
@Resource
private OrderService ordersService;
/**
* 请求参数
*/
public static RequestParam requestParam = null;
@Override
@Transactional(rollbackFor = Exception.class)
public synchronized PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount) {
// request.setXxx(val)设置所需参数具体参数可见Request定义
PrepayRequest request = new PrepayRequest();
// 金额
Amount WxAmount = new Amount();
WxAmount.setTotal(amount.movePointRight(2).intValue());
WxAmount.setCurrency("CNY");
request.setAmount(WxAmount);
// 公众号id
request.setAppid(wxPayConfig.getAppId());
// 商户号
request.setMchid(wxPayConfig.getMerchantId());
// 支付者信息
Payer payer = new Payer();
payer.setOpenid(miniOpenId);
request.setPayer(payer);
// 描述
request.setDescription("订单号:" + orderId);
// 微信回调地址
request.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/api/wechat/payment/callback");
// 商户订单号
request.setOutTradeNo(RandomUtil.randomNumbers(12));
//返回数据前端调起支付
return wxPayConfig.getJsapiServiceExtension().prepayWithRequestPayment(request);
}
@Override
public Transaction getTransactionInfo(HttpServletRequest request) {
NotificationParser notificationParser = getNotificationParser(request);
return notificationParser.parse(requestParam, Transaction.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public synchronized boolean paymentCallback(Transaction transaction) throws IOException {
System.out.println("---------------------------微信支付回调(开始)-------------------------------");
// 获取订单信息
String orderIdByString = transaction.getOutTradeNo();
Order order = ordersService.getById(orderIdByString);
if (order == null) {
log.error("订单不存在");
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "订单不存在,订单号:" + transaction.getOutTradeNo());
}
// 生成取餐码
// 获取当日的日期
LocalDate today = LocalDate.now();
// 获取当日的 00:00 时间
LocalDateTime startTime = today.atStartOfDay();
// 获取当日的 23:59 时间
LocalDateTime endTime = today.atTime(LocalTime.MAX);
// // 生成取餐码
// QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("businessId", order.getBusinessId());
// queryWrapper.between("createTime", startTime, endTime);
// queryWrapper.in("state", 1, 2);
// long count = ordersService.count(queryWrapper);
// String pickupCode = String.format("%04d", count + 1);
// // 修改订单信息
// order.setState(1);
// order.setPickupCode(pickupCode);
// Date date = new Date();
// order.setUpdateTime(date);
// boolean update = ordersService.updateById(order);
// ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "修改订单状态失败");
// // 通知商家新订单
// Business business = businessService.getById(order.getBusinessId());
// Long userId = order.getUserId();
// Long businessId = business.getUserId();
// MessageVO messageVO = new MessageVO();
// messageVO.setType(1);
// messageVO.setUserId(userId);
// messageVO.setToUserId(businessId);
// messageVO.setContent(orderIdByString);
// String message = JSONUtil.toJsonStr(messageVO);
// // 通知商家新订单
// webSocketServer.onMessage(message);
System.out.println("---------------------------微信支付回调(结束)-------------------------------");
return true;
}
@Override
public Refund refundPayment(String orderId, BigDecimal amount) {
// 退款请求
CreateRequest createRequest = new CreateRequest();
// 商户订单号
createRequest.setOutTradeNo(orderId);
// 商户退款单号
createRequest.setOutRefundNo(orderId);
// 退款结果回调
createRequest.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/api/wechat/refund/callback");
// 退款金额
AmountReq amountReq = new AmountReq();
long refundAmount = amount.movePointRight(2).intValue();
amountReq.setRefund(refundAmount);
amountReq.setTotal(refundAmount);
amountReq.setCurrency("CNY");
createRequest.setAmount(amountReq);
// 申请退款
System.out.println("退款请求:" + createRequest);
Refund refund = wxPayConfig.getRefundService().create(createRequest);
System.out.println("退款申请结果:" + refund);
return refund;
}
@Override
public RefundNotification getRefundInfo(HttpServletRequest request) {
NotificationParser notificationParser = getNotificationParser(request);
return notificationParser.parse(requestParam, RefundNotification.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public synchronized boolean refundCallback(RefundNotification refundNotification) {
System.out.println("---------------------------微信退款回调(开始)-------------------------------");
// 获取订单信息
String orderIdByString = refundNotification.getOutTradeNo();
Order order = ordersService.getById(orderIdByString);
// 修改订单信息
order.setOrderStatus("已退款");
ordersService.updateById(order);
System.out.println("---------------------------微信退款回调(结束)-------------------------------");
return true;
}
@Override
public boolean sendSubscribeMessage(String openid, String templateId) {
try {
// 获取小程序对象
WxMaService wxMaService = wxOpenConfig.getWxMaService();
// 获取消息接口
WxMaMsgService msgService = wxMaService.getMsgService();
// 构建订阅消息体
WxMaSubscribeMessage wxMaSubscribeMessage = new WxMaSubscribeMessage();
wxMaSubscribeMessage.setToUser(openid);
wxMaSubscribeMessage.setTemplateId(templateId);
List<WxMaSubscribeMessage.MsgData> msgDataList = getMsgData();
wxMaSubscribeMessage.setData(msgDataList);
// 发送消息
msgService.sendSubscribeMsg(wxMaSubscribeMessage);
return true;
} catch (Exception e) {
log.error("发送订阅消息失败:", e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "发送订阅消息失败");
}
}
/**
* 提取消息数据
*/
private static List<WxMaSubscribeMessage.MsgData> getMsgData() {
List<WxMaSubscribeMessage.MsgData> msgDataList = new ArrayList<>();
String[][] dataItems = {
{"thing1", "新订单"},
{"phrase2", "已支付"},
{"amount3", "666"},
{"thing10", "666666"},
{"time8", "2019-12-18 12:12:12"}
};
for (String[] item : dataItems) {
WxMaSubscribeMessage.MsgData msgData = new WxMaSubscribeMessage.MsgData();
msgData.setName(item[0]);
msgData.setValue(item[1]);
msgDataList.add(msgData);
}
return msgDataList;
}
/**
* 根据微信官方发送的请求获取信息
*/
@SneakyThrows
public NotificationParser getNotificationParser(HttpServletRequest request) {
System.out.println("---------------------------获取信息-------------------------------");
// 获取RSA配置
NotificationParser notificationParser = new NotificationParser(wxPayConfig.getRSAConfig());
// 构建请求
StringBuilder bodyBuilder = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
bodyBuilder.append(line);
}
String body = bodyBuilder.toString();
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String singType = request.getHeader("Wechatpay-Signature-Type");
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
requestParam = new RequestParam.Builder()
.serialNumber(wechatPayCertificateSerialNumber)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(singType)
.body(body)
.build();
System.out.println(requestParam.toString());
System.out.println("---------------------------信息获取完毕-------------------------------");
return notificationParser;
}
}

View File

@ -1,23 +0,0 @@
package com.cultural.heritage.utils;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import java.util.ArrayList;
import java.util.List;
public class AppointmentDateUtil {
// 将当天及未来三天的日期加入到集合中
public static List<String> getDates() {
List<String> dates = new ArrayList<>();
for (int i = 0; i < 4; i ++ ) {
DateTime date = DateUtil.date();
DateTime dateTime = DateUtil.offsetDay(date, i);
String formatDate = DateUtil.formatDate(dateTime);
dates.add(formatDate);
}
return dates;
}
}

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDQvDoLQ9+041aV
lLvDgIdhenuq54wx+UeuIWd2Zc4E4Io+47PCr6kzpbkLhq47//dKAG1Fobb2vCRu
HG+CUfhkOzYLxt9qC4k9TOAtQPWp3Hc3dTldw42C/pGzh8aro7rHtpRHDzQYdJgB
w4dPQ0+RoZ2U9meiGWsy2DVOydPVxabxN9gFeeP5FRKjZP2JUAoKJRgWrBD9Fgyn
mX/crOa8z5HTBK/LY+jBy57Bp9ZpUS9nDIpXSgdI7mZn3xvLmGfjki8WG9X8Ad99
jvyTzD0HDcm7XoGo+Rp2Evh0pqQQf33R/OkH1WIFtcrqfwGUugTLDfIrFvDr3Qgc
KE2Z2U7pAgMBAAECggEAXgIn8iLjgchRmpSd6/LbBh/vyoz2KxumGNqaikxXeQLX
wHM05p3OiqA8suA5YHRrnzyJ+i5XBNC/Z4gPAJaCVEIGmU88F7qSWLVi0X7MJXBR
kPyOlZgZB8I3RLAF3g+jc4bbSRWj1M/OFh1Ft4ENOP2cxxYinnLsQL33ZECp00Cc
IXlX4lqjkoFbBaeCZLEV5tzZ3R/NsYZ5HTtvEh+vX7WdIqmgfL/cndfC+fIlWqbi
kKITyfI//Ik02/07qbFaZOwCLAQccUBT/0K8v7b0rqr0XeiqN3a+lXkVY6DpFxXx
aplsZ8rljBZ51AcAw8bS/JpwMkAQ5tYbgN2luEq3MQKBgQD/x2yk6YECbutRDsFA
EOlZK1cwMbJSkCMgVxIrfzv0Ubj937CCzZ7StxiHCxAa3+Oj8uX3aL57ZEatudyU
PsbZP6WWMqD3HiWUZoD1qcqRbp57FR9a3sFkgXoN9o93xdLiUS8LVAVLaqGSfO76
o7euReXlK+hNAFmkAwFsPnVdawKBgQDQ6mWSaefUfDVddw8mPdgCtjCIKMBWTNs9
U0ygVEsDs2FDxcC8175GQBbCNvtgh7DxWfOs847TVjH2CpZk74vSyt0Y59295Xx+
t4Vn3CHCBxvYGIRCTOXjBRRUOxQLXjb+X6XN3y9pxhkhVdwr5m5q49JIWcNyNuAJ
LTqq6CLl+wKBgAjaAeyDGC/ZXtNjS1TIQQsQ8Od+EMnCqzSHTt2qfYyq91fx0c31
B7YLGBI0U85aSSp3UXYKbe0fP0Lr17JZqdAC39wezGtA49QK6BOYWKZHybxAsuEW
LGMqB+tLyRNACVhDrvkZY0WE3yqOoEaUO9sQGDCiIFvp0zBV2krArpcZAoGAHiwM
GVY0RireJi6AwJwj61hWsAN6q7wT2cqDAZDK+LDadkhEKsHZ2Bl/b/My4OEX+/Nq
zuqqEPmc45Tp3Y//GKV1wxgRnVBcZ4yntrVDJtuR+Oapi03B0cS1B+k0XuPve1Nj
BdWa6mLS1E6rKqfwAH4Aq7RTFta4Cns+wtod2CsCgYAu7gHrFJfB6C/Dl93ZXTiL
HQdxpk0+L0hmXYaHFVK+0L2lswd7jlx6NLE3CNKEbUVr2fdhi8Qr2PLqiK/sAUCD
yRN6z17oSZ6eUfax5wKO5yXkuKq9DAoWpLitLgQmrxV1xfBx/q8ojVnFAG17q3F+
lN+FSz/IRCR9pAaVuVKw0w==
-----END PRIVATE KEY-----

View File

@ -61,11 +61,26 @@ hwyun:
wx:
mini:
appId: wx3f968a09e31d6bed
appSecret: 847bdda7c2b01e88d59948b9ba50ef8d
appId: wx61b63e27bddf4ea2
appSecret: 5ef9e1f17acd8180afe2d80199fd466e
official:
appId: wx5d04ca2de0e628a8
appSecret: 495af5bc4df1b86ffcfc21bb12daea76
pay:
#应用id小程序id
appId: wx61b63e27bddf4ea2
#商户号
merchantId: 1700326544
#商户API私钥
privateKeyPath: apiclient_key.pem
#商户证书序列号
merchantSerialNumber: 6DC8953AB741D309920DA650B92F837BE38A2757
#商户APIv3密钥
apiV3Key: fbemuj4Xql7CYlQJAoTEPYxvPSNgYT2t
# 通知地址
notifyUrl: https://winning-mouse-internally.ngrok-free.app
# notifyUrl: http://localhost:9092
knife4j:

View File

@ -0,0 +1,7 @@
<?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.cultural.heritage.mapper.TimeIntervalMapper">
</mapper>

View File

@ -0,0 +1,39 @@
package com.cultural.heritage.test;
class Animal {
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Bird extends Animal {
public Bird(String name, int age) {
super(name, age);
}
public void fly() {
System.out.println(name + " 能飞行");
}
public void showBirdInfo() {
System.out.println("鸟的名称: " + name);
}
public static void main(String[] args) {
Bird bird = new Bird("麻雀", 2);
bird.showBirdInfo();
bird.fly();
}
}

View File

@ -1,5 +1,11 @@
package com.cultural.heritage.test;
public class B {
public Long num = 10L;
static {
System.out.println("静态块已执行");
}
public static void main(String[] args) {
new B(); // 创建对象观察静态块是否执行
}
}