二维码

This commit is contained in:
seesaw
2024-10-18 14:00:35 +08:00
parent 8d4f8a6706
commit 2cd8d3a7e5
11 changed files with 250 additions and 68 deletions

View File

@ -94,6 +94,19 @@
<artifactId>yudao-spring-boot-starter-websocket</artifactId>
</dependency>
<!-- 二维码 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
</project>

View File

@ -75,4 +75,10 @@ public class BusinessRespVO {
private BigDecimal reduce;
private String time;
private BigDecimal turnoverCompare;
private int orderSumCompare;
private BigDecimal priceAvgCompare;
}

View File

@ -173,6 +173,27 @@ public class AppStoreController {
return JsonUtils.toJsonString(StoreResult.success(null));
}
@PostMapping("/mgcr/order/payByCodeOrder")
@Operation(summary = "添加二维码订单")
public String payByCodeOrder(@RequestBody StoreOrderDto dto) {
String s = orderService.cardPay(dto);
if("true".equals(s)){
return JsonUtils.toJsonString(StoreResult.success(null));
} else if ("false".equals(s)) {
return JsonUtils.toJsonString(StoreResult.fail("余额不足"));
}else {
return JsonUtils.toJsonString(StoreResult.fail(s));
}
}
public String reverseHexAndConvert(String hexStr) {
// 将十六进制字符串反转,每两位一组
StringBuilder reversedHex = new StringBuilder();
@ -191,4 +212,9 @@ public class AppStoreController {
return decimalStr;
}
}

View File

@ -6,4 +6,6 @@ import lombok.Data;
public class StoreOrderDto extends StoreSaleGoodsDto{
private String cardNumber;
private String jwt;
}

View File

@ -151,5 +151,11 @@ public class AppMemberUserController {
public CommonResult<Map<String,Object>> getInfoByCard(String cardId,Long mobile){
return success(userService.getInfoByCard(cardId,mobile));
}
@GetMapping("/getQRCode")
@Operation(summary = "获取消费二维码")
public CommonResult<String> getQRCode(){
return success(userService.getQRCode());
}
}

View File

@ -104,60 +104,63 @@ public class BusinessServiceImpl implements BusinessService {
}
@Override
public synchronized void updateStatistics(StatisticsVo vo) {
// 获取今天的开始时间
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
// 获取今天的结束时间 (23:59:59.999)
LocalDateTime endOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
public void updateStatistics(StatisticsVo vo) {
synchronized(vo.getCarteenId()){
// 获取今天的开始时间
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
// 获取今天的结束时间 (23:59:59.999)
LocalDateTime endOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
List<BusinessDO> businessDOS = businessMapper.selectList(Wrappers.<BusinessDO>lambdaQuery()
.between(BusinessDO::getCreateTime, startOfDay, endOfDay)
.eq(BusinessDO::getCarteenId, vo.getCarteenId()));
if (CollectionUtil.isNotEmpty(businessDOS)) {
BusinessDO businessDO = businessDOS.get(0);
List<BusinessDO> businessDOS = businessMapper.selectList(Wrappers.<BusinessDO>lambdaQuery()
.between(BusinessDO::getCreateTime, startOfDay, endOfDay)
.eq(BusinessDO::getCarteenId, vo.getCarteenId()));
if (CollectionUtil.isNotEmpty(businessDOS)) {
BusinessDO businessDO = businessDOS.get(0);
//重量
if(ObjectUtil.isNotEmpty(vo.getOrderId())){
List<OrderDetailDO> orderDetailDOS = orderDetailMapper.selectList(Wrappers.<OrderDetailDO>lambdaQuery().eq(OrderDetailDO::getOrderId, vo.getOrderId()));
if (CollectionUtil.isNotEmpty(orderDetailDOS)) {
BigDecimal reduce = orderDetailDOS.stream().map(OrderDetailDO::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
businessDO.setWeigh(businessDO.getWeigh().add(reduce));
//重量
if(ObjectUtil.isNotEmpty(vo.getOrderId())){
List<OrderDetailDO> orderDetailDOS = orderDetailMapper.selectList(Wrappers.<OrderDetailDO>lambdaQuery().eq(OrderDetailDO::getOrderId, vo.getOrderId()));
if (CollectionUtil.isNotEmpty(orderDetailDOS)) {
BigDecimal reduce = orderDetailDOS.stream().map(OrderDetailDO::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
businessDO.setWeigh(businessDO.getWeigh().add(reduce));
}
}
}
//订单数
businessDO.setOrderSum(businessDO.getOrderSum() + vo.getOrder_sum());
//订单数
businessDO.setOrderSum(businessDO.getOrderSum() + vo.getOrder_sum());
//营业额
if(ObjectUtil.isNotEmpty(vo.getTotalMoney())){
businessDO.setTurnover(businessDO.getTurnover().add(vo.getTotalMoney()));
}
//客单价
if(businessDO.getOrderSum() != 0){
businessDO.setPriceAvg(businessDO.getTurnover().divide(new BigDecimal(businessDO.getOrderSum()), 2, RoundingMode.HALF_UP));
}
//减免金额
if(ObjectUtil.isNotEmpty(vo.getReduceMoney())){
businessDO.setReduce(businessDO.getReduce().add(vo.getReduceMoney()));
}
//早中晚金额
if(ObjectUtil.isNotEmpty(vo.getTime())){
String timePeriod = TimePeriodEnum.getTimePeriod(vo.getTime());
if(timePeriod.equals(CostTypeEnum.MORNING.getCode())){
businessDO.setBreakfast(businessDO.getBreakfast().add(vo.getTotalMoney()));
}else if (timePeriod.equals(CostTypeEnum.NOON.getCode())){
businessDO.setLunch(businessDO.getLunch().add(vo.getTotalMoney()));
}else if (timePeriod.equals(CostTypeEnum.NIGHT.getCode())){
businessDO.setDinner(businessDO.getDinner().add(vo.getTotalMoney()));
//营业额
if(ObjectUtil.isNotEmpty(vo.getTotalMoney())){
businessDO.setTurnover(businessDO.getTurnover().add(vo.getTotalMoney()));
}
}
businessMapper.updateById(businessDO);
//客单价
if(businessDO.getOrderSum() != 0){
businessDO.setPriceAvg(businessDO.getTurnover().divide(new BigDecimal(businessDO.getOrderSum()), 2, RoundingMode.HALF_UP));
}
//减免金额
if(ObjectUtil.isNotEmpty(vo.getReduceMoney())){
businessDO.setReduce(businessDO.getReduce().add(vo.getReduceMoney()));
}
//早中晚金额
if(ObjectUtil.isNotEmpty(vo.getTime())){
String timePeriod = TimePeriodEnum.getTimePeriod(vo.getTime());
if(timePeriod.equals(CostTypeEnum.MORNING.getCode())){
businessDO.setBreakfast(businessDO.getBreakfast().add(vo.getTotalMoney()));
}else if (timePeriod.equals(CostTypeEnum.NOON.getCode())){
businessDO.setLunch(businessDO.getLunch().add(vo.getTotalMoney()));
}else if (timePeriod.equals(CostTypeEnum.NIGHT.getCode())){
businessDO.setDinner(businessDO.getDinner().add(vo.getTotalMoney()));
}
}
businessMapper.updateById(businessDO);
}
}
}
@Override
@ -188,25 +191,6 @@ public class BusinessServiceImpl implements BusinessService {
// 获取当月的总天数
int daysInMonth = yearMonth.lengthOfMonth();
// 定义一个格式化器,格式为 "dd"(两位数的日期)
DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("dd");
// 循环遍历该月的每一天
for (int day = 1; day <= daysInMonth; day++) {
if(map.get(day) == null){
BusinessRespVO bean = new BusinessRespVO();
bean.setTurnover(BigDecimal.ZERO).setOrderSum(0).setReduce(BigDecimal.ZERO).setWeigh(BigDecimal.ZERO)
.setBreakfast(BigDecimal.ZERO).setLunch(BigDecimal.ZERO).setDinner(BigDecimal.ZERO)
.setTime(String.valueOf(day));
monthData.put(day,bean);
}else {
BusinessRespVO bean = BeanUtils.toBean(map.get(day), BusinessRespVO.class);
bean.setTime(String.valueOf(day));
monthData.put(day,bean);
}
}
//获取上一个月最后一天的数据
// 获取上一个月的最后一天
YearMonth previousMonth = yearMonth.minusMonths(1);
@ -230,7 +214,27 @@ public class BusinessServiceImpl implements BusinessService {
.setBreakfast(BigDecimal.ZERO).setLunch(BigDecimal.ZERO).setDinner(BigDecimal.ZERO);
}
businessDataVO.setOld(old);
// 循环遍历该月的每一天
for (int day = 1; day <= daysInMonth; day++) {
BusinessRespVO bean = new BusinessRespVO();
if(map.get(day) == null){
bean.setTurnover(BigDecimal.ZERO).setOrderSum(0).setReduce(BigDecimal.ZERO).setWeigh(BigDecimal.ZERO)
.setBreakfast(BigDecimal.ZERO).setLunch(BigDecimal.ZERO).setDinner(BigDecimal.ZERO)
.setTime(String.valueOf(day));
}else {
bean = BeanUtils.toBean(map.get(day), BusinessRespVO.class);
bean.setTime(String.valueOf(day));
}
//计算差额
if(day == 1){
compare(bean,old);
}else {
compare(bean,monthData.get(day-1));
}
monthData.put(day,bean);
}
businessDataVO.setBusinessList(new ArrayList<>(monthData.values()));
return businessDataVO;
}
@ -289,7 +293,7 @@ public class BusinessServiceImpl implements BusinessService {
List<BusinessDO> businessDOS = businessMapper.selectList(wrapper);
Map<Integer,BusinessRespVO> map = new HashMap<>();
//时间处理
for (int i = 1; i <= 12; i++) {
BusinessRespVO bean = new BusinessRespVO();
bean.setTurnover(BigDecimal.ZERO).setOrderSum(0).setReduce(BigDecimal.ZERO).setWeigh(BigDecimal.ZERO)
@ -297,6 +301,7 @@ public class BusinessServiceImpl implements BusinessService {
.setTime(String.valueOf(i));
map.put(i,bean);
}
//数据统计
for (BusinessDO businessDO : businessDOS) {
LocalDate date = businessDO.getCreateTime().toLocalDate();
@ -320,8 +325,15 @@ public class BusinessServiceImpl implements BusinessService {
for (BusinessDO businessDO : previousList) {
handleBusiness(businessDO,previousVO);
}
//差额统计
for (int i = 1; i <= 12; i++) {
if(i == 1){
compare(map.get(i),previousVO);
}else {
compare(map.get(i),map.get(i-1));
}
}
businessDataVO.setOld(previousVO);
businessDataVO.setBusinessList(new ArrayList<>(map.values()));
return businessDataVO;
@ -349,4 +361,10 @@ public class BusinessServiceImpl implements BusinessService {
businessRespVO.setCarteenId(businessDO.getCarteenId());
}
void compare(BusinessRespVO today, BusinessRespVO last) {
today.setTurnoverCompare(today.getTurnover().subtract(last.getTurnover()));
today.setOrderSumCompare(today.getOrderSum()-last.getOrderSum());
today.setPriceAvgCompare(today.getPriceAvg().subtract(last.getPriceAvg()));
}
}

View File

@ -62,5 +62,7 @@ public interface StoreOrderService {
String cardPay(StoreOrderDto dto);
String codePay(StoreOrderDto dto);
void batchCacheOrder(StoreOrderUploadDto dto);
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.member.service.storeorder;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.member.controller.admin.storeorder.vo.StoreOrderPageReqVO;
@ -35,6 +36,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.STORE_ORDER_NOT_EXISTS;
import static cn.iocoder.yudao.module.member.util.QRCodeWithJWTUtil.validateJWT;
/**
* 商品订单 Service 实现类
@ -154,6 +156,20 @@ public class StoreOrderServiceImpl implements StoreOrderService {
}
}
@Override
public String codePay(StoreOrderDto dto) {
String s = validateJWT(dto.getJwt());
if (StrUtil.isBlank(s)) {
return "false";
}
return "";
}
@Override
public void batchCacheOrder(StoreOrderUploadDto dto) {
for (StoreLocalOrderDto orderDto :dto.getMgcrCacheOrderVoList()){

View File

@ -259,4 +259,5 @@ public interface MemberUserService {
boolean delete(Long userId);
String getQRCode();
}

View File

@ -51,6 +51,7 @@ import cn.iocoder.yudao.module.member.service.orderdetail.OrderDetailService;
import cn.iocoder.yudao.module.member.service.tag.MemberTagService;
import cn.iocoder.yudao.module.member.service.userexpand.UserExpandService;
import cn.iocoder.yudao.module.member.util.MemberConstants;
import cn.iocoder.yudao.module.member.util.QRCodeWithJWTUtil;
import cn.iocoder.yudao.module.system.api.dishesnutrition.DishesNutritionApi;
import cn.iocoder.yudao.module.system.api.dishesnutrition.dto.DishesNutritionRespDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
@ -63,6 +64,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -83,12 +85,16 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.setLoginUser;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.member.util.QRCodeWithJWTUtil.generateJWT;
import static cn.iocoder.yudao.module.member.util.QRCodeWithJWTUtil.validateJWT;
/**
* 会员 User Service 实现类
@ -140,6 +146,9 @@ public class MemberUserServiceImpl implements MemberUserService {
@Resource
private MemberTagService memberTagService;
@Resource
private StringRedisTemplate memberUserRedisTemplate;
@Override
public MemberUserDO getUserByMobile(String mobile) {
@ -897,4 +906,21 @@ public class MemberUserServiceImpl implements MemberUserService {
memberUserMapper.deleteFace(userId);
return i>0;
}
@Override
public String getQRCode() {
Long userId = getLoginUserId();
String redisKey = QRCodeWithJWTUtil.QR_PREFIX+userId;
String url = memberUserRedisTemplate.opsForValue().get(redisKey);
if(StrUtil.isNotEmpty(url)){
return url;
}
String jwt = generateJWT(userId); // 1分钟后过期
String fileName = "QRCode_" + userId + ".png";
String path = QRCodeWithJWTUtil.BASE_PATH+fileName;
QRCodeWithJWTUtil.generateQRCode(jwt, 350, 350, path); // 生
memberUserRedisTemplate.opsForValue().set(redisKey,path,60, TimeUnit.SECONDS);
return path;
}
}

View File

@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.member.util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class QRCodeWithJWTUtil {
private static final String SECRET_KEY = "your_secret_key"; // 使用HS256算法生成秘钥
public static final String BASE_PATH = "qrCodes/";
public static final String QR_PREFIX = "QRCODE_";
// public static void main(String[] args) throws WriterException, IOException {
// // 用户ID
// Long userId = 123L;
//
// // 生成包含JWT的二维码
// String jwt = generateJWT(userId, 1); // 1分钟后过期
// String fileName = "QRCode_" + userId + ".png";
// generateQRCode(jwt, 350, 350, BASE_PATH+fileName); // 生成二维码并保存
// }
// 生成 JWT
public static String generateJWT(Long userId) {
// 创建 JWT payload
Map<String, Object> payload = new HashMap<>();
payload.put("userId", userId);
// 设置过期时间为1分钟
long expireTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1);
payload.put("exp", expireTime); // 添加过期时间到 payload
// 生成 JWT
return JWTUtil.createToken(payload, SECRET_KEY.getBytes());
}
// 生成二维码图片
public static void generateQRCode(String data, int width, int height, String filePath) {
QrCodeUtil.generate(data, width, height, new File(filePath));
}
// 验证JWT是否有效和未过期
public static String validateJWT(String jwt) {
boolean isValid = JWTUtil.verify(jwt, SECRET_KEY.getBytes());
if (isValid) {
System.out.println("JWT 验证成功!");
// 解析 JWT
JSONObject payloads = JWTUtil.parseToken(jwt).getPayloads();
Map<String, Object> map = JSONUtil.toBean(payloads, Map.class);
return map.get("userId").toString();
} else {
System.out.println("JWT 验证失败!");
}
return null;
}
}