1、部分效果演示
- 车辆列表
- 租车详情
- 订单列表
- jmeter压力测试
- 抢单错误信息持久化到redis
- 控制台信息
2、技术栈
功能或特点 | 名称 | 版本 |
---|---|---|
父项目依赖 | spring-boot-starter-parent | 2.2.5.RELEASE |
微服务架构 | spring-cloud-dependencies | Hoxton.RELEASE |
admin监控服务 | spring-boot-admin-starter-server | 2.2.4 |
eureka管理服务 | spring-cloud-starter-netflix-eureka-server | 2.2.0.RELEASE |
config配置服务 | spring-cloud-config-server | 2.2.0.RELEASE |
gateway网关服务 | spring-cloud-starter-gateway | 2.2.0.RELEASE |
链路追踪服务 | spring-cloud-starter-zipkin | 2.2.0.RELEASE |
redis限流 | spring-boot-starter-data-redis-reactive | 2.2.5.RELEASE |
组件调用(已集成熔断,负载均衡) | spring-cloud-starter-openfeign | 2.2.0.RELEASE |
hutool工具类 | hutool-all | 5.8.22 |
数据库curd | mybatis-plus-boot-starter | 3.5.3.2 |
短信榛子云短信 | zhenzisms | 2.0.2 |
email邮件发送 | spring-boot-starter-mail | 2.2.5.RELEASE |
mq消息队列 | rocketmq-spring-boot-starter | 2.2.1 |
阿里云oos对象储存 | aliyun-sdk-oss | 3.15.1 |
支付宝支付 | alipay-sdk-java | 4.38.111.ALL |
分布式图片管理 | minio | 8.2.2 |
3、后端集成
3.2、用户登录
- UserController.java
package com.llh.controller;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.llh.domain.User;
import com.llh.service.UserService;
import com.llh.utils.ZzyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* User: lilinhan
* DateTime: 2023/10/20 11:57
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@Autowired
RedisTemplate redisTemplate;
@Autowired
JavaMailSender javaMailSender;
@RequestMapping("/test")
public String test(){
return "hhhhhh";
}
// 验证码接口
@RequestMapping("/getCode")
public void getCode(HttpServletResponse response) throws IOException {
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(150, 60, 4, 20);
// 重新生成code
captcha.createCode();
captcha.write(response.getOutputStream());
redisTemplate.opsForValue().set("yzm", captcha.getCode(), 1, TimeUnit.MINUTES);
}
@RequestMapping("/sendSms")
public Map<String,Object> sendSms(String phone){
Map<String,Object> map = new HashMap<>();
boolean tel = phone.matches("(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}");
if (!tel) {
map.put("code", 1005);
map.put("msg", "手机号格式错误");
return map;
}
String code = RandomUtil.randomNumbers(4);
int time = 5;
boolean b = ZzyUtils.sendSms(phone, code, time);
if (!b) {
map.put("code", 1005);
map.put("msg", "短信发送失败");
return map;
} else {
redisTemplate.opsForValue().set(phone, code, time, TimeUnit.MINUTES);
map.put("code", 1001);
map.put("msg", "短信发送成功");
return map;
}
}
@RequestMapping("/login")
public Map<String,Object> login(User user){
Map<String,Object> map = new HashMap<>();
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("name",user.getName());
User userDB = userService.getOne(wrapper);
if(userDB==null){
map.put("code",1005);
map.put("msg","该用户不存在");
return map;
}
String md5Hex = DigestUtil.md5Hex(user.getPwd() + userDB.getSalt());
if(!userDB.getPwd().equals(md5Hex)){
map.put("code",1005);
map.put("msg","密码错误");
return map;
}
String code = (String) redisTemplate.opsForValue().get("yzm");
if(StrUtil.isNotEmpty(code)){
if (!code.equalsIgnoreCase(user.getCode())) {
map.put("code", 1005);
map.put("msg", "验证码错误");
return map;
}
}else {
map.put("code", 1005);
map.put("msg", "验证码已过期");
return map;
}
Map<String, Object> map1 = new HashMap<>();
map1.put("user", JSONUtil.toJsonStr(userDB));
map1.put("loginName", userDB.getName());
map1.put("userId", userDB.getId());
String token = JWTUtil.createToken(map1, "llh".getBytes());
map.put("token", token);
map.put("loginName", userDB.getName());
map.put("userId", userDB.getId());
map.put("code", 1001);
map.put("msg", "登录成功!");
return map;
}
@RequestMapping("/phoneLogin")
public Map<String,Object> phoneLogin(User user){
Map<String,Object> map = new HashMap<>();
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("name",user.getName());
User userDB = userService.getOne(wrapper);
if(userDB==null){
map.put("code",1005);
map.put("msg","该用户不存在");
return map;
}
String md5Hex = DigestUtil.md5Hex(user.getPwd() + userDB.getSalt());
if(!userDB.getPwd().equals(md5Hex)){
map.put("code",1005);
map.put("msg","密码错误");
return map;
}
String code = (String) redisTemplate.opsForValue().get(user.getPhone());
if(StrUtil.isNotEmpty(code)){
if (!code.equals(user.getCode())) {
map.put("code", 1005);
map.put("msg", "验证码错误");
return map;
}
}else {
map.put("code", 1005);
map.put("msg", "验证码已过期");
return map;
}
Map<String, Object> map1 = new HashMap<>();
map1.put("user", JSONUtil.toJsonStr(userDB));
map1.put("loginName", userDB.getName());
map1.put("userId", userDB.getId());
String token = JWTUtil.createToken(map1, "llh".getBytes());
map.put("token", token);
map.put("loginName", userDB.getName());
map.put("userId", userDB.getId());
map.put("code", 1001);
map.put("msg", "登录成功!");
return map;
}
// 注册接口
@RequestMapping("/register")
public Map<String, Object> register(User user) {
Map<String, Object> map = new HashMap<>();
boolean qq = user.getEmail().matches("[1-9][0-9]{4,10}");
// boolean email = user.getEmail().matches("^[A-Za-z0-9]+([._%+-][A-Za-z0-9]+)*@[A-Za-z0-9]+(\\\\.[A-Za-z0-9]+)*(\\\\.[A-Za-z]{2,})$");
if(!qq){
map.put("code", 1005);
map.put("msg", "QQ号格式错误");
return map;
}
boolean phone2 = user.getPhone().matches("(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}");
if (!phone2) {
map.put("code", 1005);
map.put("msg", "手机号格式错误");
return map;
}
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("phone",user.getPhone());
User userDB = userService.getOne(wrapper);
if(userDB!=null){
map.put("code", 1005);
map.put("msg", "手机号已存在");
return map;
}
if(StrUtil.isNotBlank(user.getCode())){
String code = (String) redisTemplate.opsForValue().get("yzm");
if (!code.equalsIgnoreCase(user.getCode())) {
map.put("code", 1005);
map.put("msg", "验证码错误");
return map;
}
}else {
map.put("code", 1005);
map.put("msg", "验证码已过期");
return map;
}
// 发送短信
// sendSms(user.getPhone());
String initPwd = "123";
String initName = "张三";
String salt = UUID.randomUUID().toString().substring(0, 4);
// 邮件发送
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom("llh.fool@qq.com");
mailMessage.setTo(user.getEmail()+"@qq.com");
mailMessage.setSentDate(new Date());
mailMessage.setSubject("注册成功提示");
mailMessage.setText("恭喜:"+user.getEmail()+"注册成功!\n初始用户名:"+initName+",初始密码为:"+initPwd);
javaMailSender.send(mailMessage);
user.setPwd(DigestUtil.md5Hex(initPwd + salt));
user.setName(initName);
user.setSalt(salt);
boolean save = userService.save(user);
if (save) {
map.put("code", 1001);
map.put("msg", "注册成功");
return map;
}
return map;
}
}
3.2、车辆基本crud
- 车辆curd,CarController.java
package com.llh.controller;
import cn.hutool.core.lang.Snowflake;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.llh.domain.Car;
import com.llh.service.CarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* User: lilinhan
* DateTime: 2023/10/30 18:39
*/
@RestController
@RequestMapping("/car")
public class CarController {
@Autowired
CarService carService;
// 更新车辆
@RequestMapping("/update")
public Map<String,Object> update(Car car){
Map<String,Object> map = new HashMap<>();
boolean b = carService.updateById(car);
if(b){
map.put("flag",b);
map.put("msg","修改成功");
return map;
}else {
map.put("flag",b);
map.put("msg","修改失败");
return map;
}
}
// 车辆下架
@RequestMapping("/del")
public Map<String,Object> del(Integer id){
Map<String,Object> map = new HashMap<>();
boolean b = carService.removeById(id);
if(b){
map.put("flag",b);
map.put("msg","删除成功");
return map;
}else {
map.put("flag",b);
map.put("msg","删除失败");
return map;
}
}
// 通过订单编号获取车辆信息
@RequestMapping("/updateCarByNo")
public void updateCarByNo(String no){
carService.updateCarByNo(no);
}
// 车辆列表+查询
@RequestMapping("/list")
public Page<Car> list(Page<Car> page, Car car){
return carService.getList(page,car);
}
// 车辆录入
@RequestMapping("/save")
public Map<String,Object> save(Car car){
Map<String,Object> map = new HashMap<>();
// 编号使用雪花算法
car.setNum(new Snowflake().nextIdStr());
car.setCreateTime(new Date());
car.setSpeed("手自一体");
car.setRent(new BigDecimal(2000));
boolean save = carService.save(car);
map.put("flag",save);
map.put("msg","添加成功");
return map;
}
}
- 下拉框数据,DistController.java
package com.llh.controller;
import com.llh.domain.Dist;
import com.llh.service.DistService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* User: lilinhan
* DateTime: 2023/10/30 18:42
*/
@RestController
@RequestMapping("/dist")
public class DistController {
@Autowired
DistService distService;
@GetMapping("/list")
public Map<String, List<Dist>> list(){
Map<String,List<Dist>> map = new HashMap<>();
List<Dist> list = distService.list();
// 获取车系、车型、品牌和颜色下拉框
map.put("models",list.stream().filter(dist -> dist.getDistType().equals("car_model")).collect(Collectors.toList()));
map.put("brands",list.stream().filter(dist -> dist.getDistType().equals("car_brand")).collect(Collectors.toList()));
map.put("dists",list.stream().filter(dist -> dist.getDistType().equals("car_dist")).collect(Collectors.toList()));
map.put("colors",list.stream().filter(dist -> dist.getDistType().equals("car_color")).collect(Collectors.toList()));
return map;
}
}
3.3、图片上传
- 使用minio上传,UploadController.java
package com.llh.controller;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* User: lilinhan
* DateTime: 2023/10/24 10:34
*/
@RestController
@RequestMapping("/upload")
public class UploadController {
public static final String MINIO_SERVER = "http://127.0.0.1:9000";
private static final String BUCKET_NAME = "images";
@RequestMapping("/minio")
public Map<String, Object> minio(MultipartFile file) {
Map<String, Object> map = new HashMap<>();
// 创建客户端
MinioClient minioClient = MinioClient.builder()
.endpoint(MINIO_SERVER) // 访问url
.credentials("EGQVyIa2qbjWAKZzGZ8T","SqEHCH3elRayyazcTXESAsrozAWTOmtrIX7SssSB") // 密钥
.build();
String fileName = UUID.randomUUID() + "-" + file.getOriginalFilename();
try {
// 使用自带方法 putObject
minioClient.putObject(PutObjectArgs.builder()
.bucket(BUCKET_NAME) // 桶的名称
.object(fileName) // 文件名称
.contentType(file.getContentType()) // 文件类型
.stream(file.getInputStream(), file.getSize(), -1) // 文件流传输
.build());
map.put("code",1001);
map.put("msg","上传成功");
map.put("url",MINIO_SERVER+"/"+BUCKET_NAME+"/"+fileName);
return map;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
- 使用阿里云oss对象储存,AlyOssUtils.java
package com.llh.utils;
import cn.hutool.core.lang.UUID;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CannedAccessControlList;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
/**
* User: lilinhan
* DateTime: 2023/11/2 10:07
*/
public class AlyOssUtils {
private static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";// oss实例的地域节点
private static final String ACCESS_KEY_ID = "LTAI5t8M6dewZALaTRb3QEC7";// 阿里云密钥ID
private static final String ACCESS_KEY_SECRET = "9iG7iCRwATM0cVB8kPxmPSTbhqotqk";// 阿里云密钥
private static final String BUCKET_NAME = "llh-images";// oss实例的桶名
/**
* 图片上传(包含文件夹)
* @param file 文件
* @param model 阿里云的文件夹名
* @return map集合
*/
public static Map<String,Object> uploadToModel(MultipartFile file,String model){
Map<String,Object> map = new HashMap<>();
String url = "";
try {
// 创建ossClient实例
OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID,ACCESS_KEY_SECRET);
// 判断oss实例是否存在
if(!ossClient.doesBucketExist(BUCKET_NAME)){
// 创建桶名
ossClient.createBucket(BUCKET_NAME);
// 设置oss实例的访问权限:公共读
ossClient.setBucketAcl(BUCKET_NAME, CannedAccessControlList.PublicRead);
}
// 获取文件名
String filename = UUID.randomUUID() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
// 文件根路径
String key = model+"/"+filename;
// 将文件上传到阿里云
ossClient.putObject(BUCKET_NAME,key,file.getInputStream());
// 获取url地址
url = "https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.lastIndexOf("//")+2)+"/"+key;
// 关闭客户端
ossClient.shutdown();
map.put("code",1001);
map.put("url",url);
map.put("msg","上传成功");
return map;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 图片上传
* @param file 上传的文件
* @return map集合
*/
public static Map<String,Object> upload(MultipartFile file){
Map<String,Object> map = new HashMap<>();
String url = "";
try {
// 创建ossClient实例
OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID,ACCESS_KEY_SECRET);
// 判断oss实例是否存在
if(!ossClient.doesBucketExist(BUCKET_NAME)){
// 创建桶名
ossClient.createBucket(BUCKET_NAME);
// 设置oss实例的访问权限:公共读
ossClient.setBucketAcl(BUCKET_NAME, CannedAccessControlList.PublicRead);
}
// 获取文件名
String filename = UUID.randomUUID() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
// 将文件上传到阿里云
ossClient.putObject(BUCKET_NAME,filename,file.getInputStream());
// 获取url地址
url = "https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.lastIndexOf("//")+2)+"/"+filename;
// 关闭客户端
ossClient.shutdown();
map.put("code",1001);
map.put("url",url);
map.put("msg","上传成功");
return map;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3.4、出租功能
- 订单的curd,OrderController.java
package com.llh.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.crypto.digest.DigestUtil;
import com.llh.domain.Order;
import com.llh.service.CarService;
import com.llh.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* User: lilinhan
* DateTime: 2023/10/31 15:25
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
OrderService orderService;
@Resource
CarService carService;
@Autowired
RedisTemplate redisTemplate;
// 通过编号更新订单
@RequestMapping("/updateOrderByNo")
public void updateOrderByNo(String no){
orderService.updateOrderByNo(no);
}
// 生成订单
@RequestMapping("/save")
public Map<String,Object> save(Order order){
Map<String,Object> map = new HashMap<>();
String md5Hex = DigestUtil.md5Hex(order + "");
Boolean b = redisTemplate.opsForValue().setIfAbsent(md5Hex, md5Hex, 1, TimeUnit.MINUTES);
if(!b){
map.put("flag",false);
map.put("msg","一分钟禁止重复提交");
return map;
}
// 订单编号
String no = new Snowflake().nextIdStr();
// 计算租期
long days = DateUtil.betweenDay(order.getCarStart(), order.getCarEnd(), false);
// 租金的费用
order.setCarPrice(order.getCarRent().multiply(new BigDecimal(days)));
order.setNum(no);
order.setCreateTime(new Date());
// 录入订单
boolean save = orderService.save(order);
// 修改车辆表状态
carService.updateCarByNo(order.getCarNo());
map.put("flag",save);
map.put("msg","生成订单成功");
return map;
}
// 订单列表
@RequestMapping("/list")
public List<Order> list(){
return orderService.list();
}
}
3.5、支付功能
- PayController.java
package com.llh.controller;
import cn.hutool.extra.servlet.ServletUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.llh.domain.Order;
import com.llh.service.OrderService;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: lilinhan
* DateTime: 2023/10/31 18:51
*/
@RestController
@RequestMapping("/pay")
public class PayController {
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
@Autowired
OrderService orderService;
@Autowired
RocketMQTemplate rocketMQTemplate;
@Autowired
HttpServletResponse servletResponse;
@Autowired
HttpServletRequest request;
// 前往支付
@RequestMapping("/goPay")
public void goPay(String no) throws AlipayApiException, IOException {
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("num", no);
Order orderDB = orderService.getOne(wrapper);
Message<String> message = MessageBuilder.withPayload(no).build();
// 异步发送mq
rocketMQTemplate.asyncSend("car2", message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {
System.err.println("发送成功");
}
}
@Override
public void onException(Throwable e) {
System.err.println("连接失败");
}
});
String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLHDjvEn6fjWNOV/SGgkNv7B0AsgTA1WuKypedU2WIJ4hE5ZvJI5NiYgyh/O/HeFPYPhv+P7wv1+ATKGc5yyvb7I2b8/3MZhxFYMgn/4HjgJ9EGGfSSIkM5JqoZFHRuUNu5I1a9PqTW7SaReRdmXs3axVuHd+MYLh9KVGJyAiueAtQwq040EZHHz5lQ9oSj/R7zPEl4jXtXKHq4MC/tRl/wqAYeKwMeati9iUhK9BHYzxeTiV08l6UrMZteQ7duNxJTIg/uLQdgb7DwaWcBgy514Ra50PKmpXlHasIwhXfnW58aSpVjzdnbYu290JsGXW9h5CRp1VxZlqDfeg7zC95AgMBAAECggEAemldWFNIBZLfXiNb07lxKl31r5T3RdEID1vzSsgGQme3LBl4muipWxu4zhrLzRV/gnw1Gehv6xHl5jXZkCvO7nocqq1sGp+IJSzjNP9MByw5+iwXwB2ALE5GIgQVU53ZTw3jrbSBg4ZhSJhUOmFC4iNi+kFjZB4AenMPg1T4/rzqPPI17FO8MsW1CYiRmQcajkSDm37WXEayOaeSUXbDbxwUU96sNYU96kOuS92rX05hiZ+mX4XtKLqPuUVp0OKHe7WVEnf4sQp0nnZV4FjIS1MFk/dG24RfXBNtAkkA9m0NRxC0MTPEc2bwbjJ6bRyaETLMdzEN5d2qgCBYQzwJWQKBgQDQnHtx9Sg43XeSgqssRNaRkqC+naqyccorSfa5MTzxYfU1ByWlI4/SFfqHAbGTv2i6M191tOqffGHRHQUnyv8pSNwEbTUaQ7qZO03KiRc5cKw21U9UPrhUM8Lb5l7W2hrkpcLf5HOHOwqyMy215M84G8AsMb2vFhTD0+zh4fWHWwKBgQCqtf+kvhrO8bQQ69lgow/RB1g06VCM7I8a8WCWeLjbNwJujpcBmZA+ozVydmTsa3RNqaBfcUe6V5qQ8HCfLU4M9mKAd1cZSpP0/gwxjXBanT0pW2IaqfCJnwSZE602hNJhaDcIo0UEa94EK0TD67bpjqbPmrkt8GADjxGj3jfwuwKBgQCxSHrEErL2+IcYdIGhonKyzehbdcRN19QWy8moAocnH7dyNDuyxrD/ufvpZfASfrUyoFv8vR4zIVJDkyUBd0s3O5r3DBP32R0oAbUPbUfWYcGI6+h50L93l3F+zZ1Fe58lNxWQjiX8A5oIbOPo06Wiqjq7lB3+QIavEVgbcprlRwKBgB5D5A3uEkysN3NkjQaAAlbZyX6f+sLxUbHCJ5Cl2Igs6YZ2SzliY1tnjhs7+EqeBmGaeC9ToxbbXzDhH+ZontTXfcEu50c5nJ+rxremTswR4n0JkYwOovGTOrp07Zmj1LSmSFOEtsDA6vysOyXNDYG848Acu0X0vuECpWr7A8nvAoGBAJwEnEs86l0koOSo/DVVaVudJkMXhvl9GT7wG/MiEvknjTWosdVRgquEzGjmE25eK/4epOCONVU7bweloLSLbZ/OGGPGS4jHhJ3Kw/e4KBn4+nCi2g2/7CLfGvaydXmhgRy4qjIATfLceXtXaBNj7HIC1kCwzSphFwPzSM6DDEU8";
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi-sandbox.dl.alipaydev.com/gateway.do");
alipayConfig.setAppId("9021000130615233");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl("http://127.0.0.1:9999/car-api/pay/callback");
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(orderDB.getNum());
model.setTotalAmount(orderDB.getCarPrice() + "");
model.setSubject(orderDB.getCarNo());
model.setProductCode("FAST_INSTANT_TRADE_PAY");
request.setBizModel(model);
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
String body = response.getBody();
if (response.isSuccess()) {
System.err.println("调用成功");
servletResponse.setContentType("text/html;charset=utf8");
PrintWriter writer = servletResponse.getWriter();
writer.write(body);
writer.flush();
writer.close();
} else {
System.err.println("调用失败");
}
}
// 回调方法
@RequestMapping("/callback")
public void callback(String out_trade_no) throws IOException, AlipayApiException {
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("num", out_trade_no);
Order orderDB = orderService.getOne(wrapper);
orderDB.setStat(1);
orderDB.setRemark("已支付");
orderService.updateById(orderDB);
// 使用hutool把参数转换为map集合
Map<String, String> params = ServletUtil.getParamMap(request);
// 验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2"); //调用SDK验证签名
if (signVerified) {
System.err.println("回滚的订单编号:" + out_trade_no);
servletResponse.sendRedirect("http://127.0.0.1:8080/order");
}
}
// 退款
@RequestMapping("/refund")
public Map<String,Object> refund(String no) throws AlipayApiException {
Map<String,Object> map = new HashMap<>();
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("num", no);
Order orderDB = orderService.getOne(wrapper);
String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLHDjvEn6fjWNOV/SGgkNv7B0AsgTA1WuKypedU2WIJ4hE5ZvJI5NiYgyh/O/HeFPYPhv+P7wv1+ATKGc5yyvb7I2b8/3MZhxFYMgn/4HjgJ9EGGfSSIkM5JqoZFHRuUNu5I1a9PqTW7SaReRdmXs3axVuHd+MYLh9KVGJyAiueAtQwq040EZHHz5lQ9oSj/R7zPEl4jXtXKHq4MC/tRl/wqAYeKwMeati9iUhK9BHYzxeTiV08l6UrMZteQ7duNxJTIg/uLQdgb7DwaWcBgy514Ra50PKmpXlHasIwhXfnW58aSpVjzdnbYu290JsGXW9h5CRp1VxZlqDfeg7zC95AgMBAAECggEAemldWFNIBZLfXiNb07lxKl31r5T3RdEID1vzSsgGQme3LBl4muipWxu4zhrLzRV/gnw1Gehv6xHl5jXZkCvO7nocqq1sGp+IJSzjNP9MByw5+iwXwB2ALE5GIgQVU53ZTw3jrbSBg4ZhSJhUOmFC4iNi+kFjZB4AenMPg1T4/rzqPPI17FO8MsW1CYiRmQcajkSDm37WXEayOaeSUXbDbxwUU96sNYU96kOuS92rX05hiZ+mX4XtKLqPuUVp0OKHe7WVEnf4sQp0nnZV4FjIS1MFk/dG24RfXBNtAkkA9m0NRxC0MTPEc2bwbjJ6bRyaETLMdzEN5d2qgCBYQzwJWQKBgQDQnHtx9Sg43XeSgqssRNaRkqC+naqyccorSfa5MTzxYfU1ByWlI4/SFfqHAbGTv2i6M191tOqffGHRHQUnyv8pSNwEbTUaQ7qZO03KiRc5cKw21U9UPrhUM8Lb5l7W2hrkpcLf5HOHOwqyMy215M84G8AsMb2vFhTD0+zh4fWHWwKBgQCqtf+kvhrO8bQQ69lgow/RB1g06VCM7I8a8WCWeLjbNwJujpcBmZA+ozVydmTsa3RNqaBfcUe6V5qQ8HCfLU4M9mKAd1cZSpP0/gwxjXBanT0pW2IaqfCJnwSZE602hNJhaDcIo0UEa94EK0TD67bpjqbPmrkt8GADjxGj3jfwuwKBgQCxSHrEErL2+IcYdIGhonKyzehbdcRN19QWy8moAocnH7dyNDuyxrD/ufvpZfASfrUyoFv8vR4zIVJDkyUBd0s3O5r3DBP32R0oAbUPbUfWYcGI6+h50L93l3F+zZ1Fe58lNxWQjiX8A5oIbOPo06Wiqjq7lB3+QIavEVgbcprlRwKBgB5D5A3uEkysN3NkjQaAAlbZyX6f+sLxUbHCJ5Cl2Igs6YZ2SzliY1tnjhs7+EqeBmGaeC9ToxbbXzDhH+ZontTXfcEu50c5nJ+rxremTswR4n0JkYwOovGTOrp07Zmj1LSmSFOEtsDA6vysOyXNDYG848Acu0X0vuECpWr7A8nvAoGBAJwEnEs86l0koOSo/DVVaVudJkMXhvl9GT7wG/MiEvknjTWosdVRgquEzGjmE25eK/4epOCONVU7bweloLSLbZ/OGGPGS4jHhJ3Kw/e4KBn4+nCi2g2/7CLfGvaydXmhgRy4qjIATfLceXtXaBNj7HIC1kCwzSphFwPzSM6DDEU8";
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi-sandbox.dl.alipaydev.com/gateway.do");
alipayConfig.setAppId("9021000130615233");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setRefundAmount(orderDB.getCarPrice()+"");
model.setOutTradeNo(orderDB.getNum());
request.setBizModel(model);
AlipayTradeRefundResponse response = alipayClient.execute(request);
System.out.println(response.getBody());
if (response.isSuccess()) {
System.out.println("调用成功");
orderDB.setStat(2);
orderDB.setRemark("已退款");
boolean b = orderService.updateById(orderDB);
map.put("flag",b);
map.put("msg","退款成功");
return map;
} else {
map.put("flag",false);
map.put("msg","调用失败");
return map;
}
}
// 核对订单
@RequestMapping("/check")
public Map<String,Object> check() throws AlipayApiException {
Map<String,Object> map = new HashMap<>();
List<Order> list = orderService.list();
for (Order order : list) {
String no = order.getNum();
if(order.getStat()!=2){
String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLHDjvEn6fjWNOV/SGgkNv7B0AsgTA1WuKypedU2WIJ4hE5ZvJI5NiYgyh/O/HeFPYPhv+P7wv1+ATKGc5yyvb7I2b8/3MZhxFYMgn/4HjgJ9EGGfSSIkM5JqoZFHRuUNu5I1a9PqTW7SaReRdmXs3axVuHd+MYLh9KVGJyAiueAtQwq040EZHHz5lQ9oSj/R7zPEl4jXtXKHq4MC/tRl/wqAYeKwMeati9iUhK9BHYzxeTiV08l6UrMZteQ7duNxJTIg/uLQdgb7DwaWcBgy514Ra50PKmpXlHasIwhXfnW58aSpVjzdnbYu290JsGXW9h5CRp1VxZlqDfeg7zC95AgMBAAECggEAemldWFNIBZLfXiNb07lxKl31r5T3RdEID1vzSsgGQme3LBl4muipWxu4zhrLzRV/gnw1Gehv6xHl5jXZkCvO7nocqq1sGp+IJSzjNP9MByw5+iwXwB2ALE5GIgQVU53ZTw3jrbSBg4ZhSJhUOmFC4iNi+kFjZB4AenMPg1T4/rzqPPI17FO8MsW1CYiRmQcajkSDm37WXEayOaeSUXbDbxwUU96sNYU96kOuS92rX05hiZ+mX4XtKLqPuUVp0OKHe7WVEnf4sQp0nnZV4FjIS1MFk/dG24RfXBNtAkkA9m0NRxC0MTPEc2bwbjJ6bRyaETLMdzEN5d2qgCBYQzwJWQKBgQDQnHtx9Sg43XeSgqssRNaRkqC+naqyccorSfa5MTzxYfU1ByWlI4/SFfqHAbGTv2i6M191tOqffGHRHQUnyv8pSNwEbTUaQ7qZO03KiRc5cKw21U9UPrhUM8Lb5l7W2hrkpcLf5HOHOwqyMy215M84G8AsMb2vFhTD0+zh4fWHWwKBgQCqtf+kvhrO8bQQ69lgow/RB1g06VCM7I8a8WCWeLjbNwJujpcBmZA+ozVydmTsa3RNqaBfcUe6V5qQ8HCfLU4M9mKAd1cZSpP0/gwxjXBanT0pW2IaqfCJnwSZE602hNJhaDcIo0UEa94EK0TD67bpjqbPmrkt8GADjxGj3jfwuwKBgQCxSHrEErL2+IcYdIGhonKyzehbdcRN19QWy8moAocnH7dyNDuyxrD/ufvpZfASfrUyoFv8vR4zIVJDkyUBd0s3O5r3DBP32R0oAbUPbUfWYcGI6+h50L93l3F+zZ1Fe58lNxWQjiX8A5oIbOPo06Wiqjq7lB3+QIavEVgbcprlRwKBgB5D5A3uEkysN3NkjQaAAlbZyX6f+sLxUbHCJ5Cl2Igs6YZ2SzliY1tnjhs7+EqeBmGaeC9ToxbbXzDhH+ZontTXfcEu50c5nJ+rxremTswR4n0JkYwOovGTOrp07Zmj1LSmSFOEtsDA6vysOyXNDYG848Acu0X0vuECpWr7A8nvAoGBAJwEnEs86l0koOSo/DVVaVudJkMXhvl9GT7wG/MiEvknjTWosdVRgquEzGjmE25eK/4epOCONVU7bweloLSLbZ/OGGPGS4jHhJ3Kw/e4KBn4+nCi2g2/7CLfGvaydXmhgRy4qjIATfLceXtXaBNj7HIC1kCwzSphFwPzSM6DDEU8";
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi-sandbox.dl.alipaydev.com/gateway.do");
alipayConfig.setAppId("9021000130615233");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(no);
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
System.out.println(response.getBody());
if (response.isSuccess()) {
// 已支付,状态码未改
System.out.println("调用成功");
if(order.getStat()==1){
order.setRemark("已支付");
}else {
order.setRemark("已支付,状态码未改");
}
} else {
// 未支付,状态码已改
System.out.println("调用失败");
if(order.getStat()==0){
}else {
order.setRemark("未支付,状态码已改");
}
}
}
}
boolean b = orderService.updateBatchById(list);
if(b){
map.put("flag",b);
map.put("msg","校验成功");
return map;
}else {
map.put("flag",false);
map.put("msg","校验失败");
return map;
}
}
}
- mq消费者,MyListener.java
package com.llh.listener;
import cn.hutool.core.collection.CollUtil;
import com.llh.service.OrderService;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* User: lilinhan
* DateTime: 2023/10/31 19:16
*/
@Component
public class MyListener implements MessageListenerOrderly {
@Autowired
RedisTemplate redisTemplate;
@Autowired
OrderService orderService;
// 初始化mq
@PostConstruct
public void init() throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer();
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.subscribe("car2", "*");
consumer.setConsumerGroup("dsf");
consumer.setInstanceName("name");
consumer.registerMessageListener(this);
consumer.start();
}
// 接收的有序消息队列
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
if(CollUtil.isNotEmpty(msgs)){
for (MessageExt msg : msgs) {
String body = new String(msg.getBody());
System.err.println("接受的消息--------"+body);
return ConsumeOrderlyStatus.SUCCESS;
}
}else {
System.err.println("消息为空");
}
return ConsumeOrderlyStatus.SUCCESS;
}
}
3.6、模拟抢单
- 配置类,MybatisPlusConfig.java
package com.llh.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* User: lilinhan
* DateTime: 2023/10/13 11:53
*/
@Configuration
@MapperScan(value = {"com.llh.mapper"})
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 分页拦截器
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);// 核心线程数量
taskExecutor.setMaxPoolSize(10);// 最大线程数量
taskExecutor.setKeepAliveSeconds(2);// 设置时长(秒)
taskExecutor.setQueueCapacity(10);// 设置队列容量
taskExecutor.setThreadNamePrefix("llh-thread:");// 线程名前缀
return taskExecutor;
}
}
- ThreadController.java
package com.llh.controller;
import cn.hutool.core.lang.Snowflake;
import com.llh.domain.Takeaway;
import com.llh.service.TakeawayService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.*;
/**
* User: lilinhan
* DateTime: 2023/11/2 14:04
*/
@RestController
@RequestMapping("/thread")
public class ThreadController {
@Autowired
TakeawayService takeawayService;
@Autowired
RedisTemplate redisTemplate;
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
RedissonClient redissonClient;
private int count = 1;
// 生成订单
@RequestMapping("/test")
public boolean test(){
for (int i = 0; i < 3; i++) {
threadPoolTaskExecutor.execute(new Runnable() {
@Override
public void run() {
List<Takeaway> list = new ArrayList<>();
for (int i1 = 0; i1 < 5; i1++) {
System.out.println("当前线程-----"+Thread.currentThread().getId()+":"+Thread.currentThread().getName());
// 生成实体
Takeaway takeaway = new Takeaway();
takeaway.setPrice(new BigDecimal(20.22));
takeaway.setFroms(new Snowflake().nextIdStr());
takeaway.setTos(new Snowflake().nextIdStr());
takeaway.setCreated(new Date());
list.add(takeaway);
}
takeawayService.saveBatch(list);
}
});
}
return true;
}
// 测试抢单
@RequestMapping("/size")
public Map<String,Object> size(String no){
Map<String,Object> map = new HashMap<>();
System.err.println("当前用户:"+Thread.currentThread().getId()+",正在抢购商品:"+no);
// Boolean b1 = redisTemplate.opsForValue().setIfAbsent(no, no, 2, TimeUnit.SECONDS);
RLock lock = redissonClient.getLock(no);
if(lock.tryLock()){
try {
// 通过id查询订单
Takeaway takeaway = takeawayService.getById(no);
takeaway.setStat(1);// 已抢
Thread.sleep(3000);
boolean b = takeawayService.updateById(takeaway);
if(takeaway.getStat()==1){
System.err.println("此商品已被抢购");
}
if(b){
System.err.println("恭喜用户:"+Thread.currentThread().getId()+",@@@@@@成功抢购到商品:"+no);
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
if(lock!=null && lock.isHeldByCurrentThread()){
lock.unlock();;
}
}
}else {
System.err.println("抢购结束");
}
return map;
}
}
4、前端代码
- 用户登录,LoginView.vue
<script>
import qs from "qs";
export default {
data(){
return{
formInline:{},
formInline2:{},
formInline3:{},
activeName: 'easyLogin',
codeUrl:'http://127.0.0.1:9999/user-api/user/getCode',
rules:{
name:[
{ required: true, message: '请输入账号', trigger: 'blur' },
],
phone:[
{ required: true, message: '请输入手机号', trigger: 'blur' },
],
pwd:[
{ required: true, message: '请输入密码', trigger: 'blur' },
],
code:[
{ required: true, message: '请输入验证码', trigger: 'blur' },
],
email:[
{ required: true, message: '请输入邮箱', trigger: 'blur' },
]
}
}
},
methods:{
handleClick(tab, event) {
console.log(tab, event);
},
newCode(){
this.codeUrl = 'http://127.0.0.1:9999/user-api/user/getCode?time='+new Date().getTime();
},
onSubmit(loginForm){
this.$refs[loginForm].validate((val)=>{
if(val){
this.axios.post("/user-api/user/login",qs.stringify(this.formInline)).then(res=>{
if(res.data.code==1001){
this.$message.success(res.data.msg);
localStorage.setItem("token",res.data.token);
localStorage.setItem("loginName", res.data.loginName)
localStorage.setItem("userId", res.data.userId)
this.$router.push("/good");
}else{
this.$message.error(res.data.msg);
this.newCode();
this.formInline.code = '';
}
})
}else {
return false;
}
})
},
sendSms(tel){
this.axios.post("/user-api/user/sendSms?phone="+tel).then(res=>{
if(res.data.code==1001){
this.$message.success(res.data.msg);
}else {
this.$message.error(res.data.msg);
}
})
},
onSubmit2(loginForm){
this.$refs[loginForm].validate((val)=>{
if(val){
this.axios.post("/user-api/user/phoneLogin",qs.stringify(this.formInline2)).then(res=>{
if(res.data.code==1001){
this.$message.success(res.data.msg);
localStorage.setItem("token",res.data.token);
this.$router.push("/list");
}else{
this.$message.error(res.data.msg);
this.formInline2.code = '';
}
})
}else {
return false;
}
})
},
Register(loginForm){
this.$refs[loginForm].validate((val)=>{
if(val){
this.axios.post("/user-api/user/register",qs.stringify(this.formInline3)).then(res=>{
if(res.data.code==1001){
this.$message.success(res.data.msg);
this.activeName = "easyLogin";
}else {
this.$message.error(res.data.msg);
}
})
}else {
return false;
}
})
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
},
created() {
}
}
</script>
<template>
<div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="普通登录" name="easyLogin">
<el-form :inline="true" :rules="rules" ref="formInline" :model="formInline" class="demo-form-inline">
<el-form-item label="账号" prop="name">
<el-input v-model="formInline.name" placeholder="账号"></el-input>
</el-form-item><br>
<el-form-item label="密码" prop="pwd">
<el-input v-model="formInline.pwd" placeholder="密码"></el-input>
</el-form-item><br>
<el-form-item label="验证码" prop="code">
<el-input v-model="formInline.code" placeholder="验证码"></el-input>
</el-form-item><br>
<el-form-item>
<el-image @click="newCode" :src="codeUrl"></el-image>
</el-form-item><br>
<el-form-item>
<el-button type="primary" @click="onSubmit('formInline')">登录</el-button>
<el-button @click="resetForm('formInline')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="手机号加验证码登录" name="telAndYzm">
<el-form :inline="true" :rules="rules" ref="formInline2" :model="formInline2" class="demo-form-inline">
<el-form-item label="手机号" prop="phone">
<el-input v-model="formInline2.phone" placeholder="手机号"></el-input>
</el-form-item><br>
<el-form-item label="验证码" prop="code">
<el-input v-model="formInline2.code" placeholder="验证码"></el-input>
</el-form-item><br>
<el-form-item>
<el-button type="primary" @click="sendSms(formInline2.phone)">发送短信</el-button>
<el-button type="primary" @click="onSubmit2('formInline2')">登录</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="邮箱注册" name="register">
<el-form :inline="true" :rules="rules" ref="formInline3" :model="formInline3" class="demo-form-inline">
<el-form-item label="邮箱" prop="email">
<el-input v-model="formInline3.email" placeholder="QQ号">
<template slot="append">@qq.com</template>
</el-input>
</el-form-item><br>
<el-form-item label="手机号" prop="phone">
<el-input v-model="formInline3.phone" placeholder="手机号"></el-input>
</el-form-item><br>
<el-form-item label="验证码" prop="code">
<el-input v-model="formInline3.code" placeholder="验证码"></el-input>
</el-form-item><br>
<el-form-item>
<el-image @click="newCode" :src="codeUrl"></el-image>
</el-form-item><br>
<el-form-item>
<el-button type="primary" @click="Register('formInline3')">注册</el-button>
<el-button @click="resetForm('formInline3')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</template>
<style scoped>
</style>
- 车辆列表,ListView.vue
<script>
import qs from "qs";
export default {
data(){
return{
formInline:{},
current:1,
size:3,
models:[],
brands:[],
dists:[],
colors:[],
tableData:[],
total:0,
addDialogFormVisible:false,
updateDialogFormVisible:false,
orderDialogFormVisible:false,
formLabelWidth:'150px',
addForm:{},
updateForm:{
imgUrl:''
},
orderForm:{
userPhone:'',
carStart:'',
carEnd:'',
carRent:0,
},
imageUrl:'',
value:'',
}
},
methods:{
updateCar(){
this.axios.post("/car-api/car/update",qs.stringify(this.updateForm)).then(res=>{
let flag = res.data.flag;
if(flag){
this.$message.success(res.data.msg);
this.updateDialogFormVisible = false;
this.onSubmit();
}else {
this.$message.success(res.data.msg);
}
})
},
openUpdate(row){
this.updateForm = row;
this.imageUrl = row.imgUrl;
this.updateDialogFormVisible = true;
},
delCar(id){
this.axios.post("/car-api/car/del?id="+id).then(res=>{
if(res.data.flag){
this.$message.success(res.data.msg);
this.onSubmit();
}else {
this.$message.success(res.data.msg);
}
})
},
saveOrder(){
this.orderForm.carStart = this.value[0];
this.orderForm.carEnd = this.value[1];
this.axios.post("/car-api/order/save",qs.stringify(this.orderForm)).then(res=>{
let flag = res.data.flag;
if(flag){
this.$message.success(res.data.msg);
this.orderDialogFormVisible = false;
this.$router.push("/order")
}else {
this.$message.success(res.data.msg);
}
})
},
goCar(row){
this.orderForm.carNo = row.num;
this.orderForm.userPhone = row.userPhone;
this.orderForm.carRent = row.rent;
this.orderDialogFormVisible = true;
},
openAdd(){
this.addForm = {};
this.addDialogFormVisible = true;
},
saveCar(){
this.axios.post("/car-api/car/save",qs.stringify(this.addForm)).then(res=>{
let flag = res.data.flag;
if(flag){
this.$message.success(res.data.msg);
this.addDialogFormVisible = false;
this.onSubmit();
}else {
this.$message.success(res.data.msg);
}
})
},
handleAvatarSuccess(res, file) {
this.imageUrl = URL.createObjectURL(file.raw);
this.addForm.imgUrl = res.url;
this.updateForm.imgUrl = res.url;
},
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.size = val;
this.onSubmit();
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.current = val;
this.onSubmit();
},
initList(){
this.axios.get("/car-api/dist/list").then(res=>{
this.models = res.data.models;
this.brands = res.data.brands;
this.dists = res.data.dists;
this.colors = res.data.colors;
})
},
onSubmit(){
this.axios.post("/car-api/car/list?current="+this.current+"&size="+this.size,qs.stringify(this.formInline)).then(res=>{
this.tableData = res.data.records;
this.total = res.data.total;
})
}
},
created() {
this.initList();
this.onSubmit();
}
}
</script>
<template>
<div>
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item label="车辆品牌">
<el-select v-model="formInline.brandId" placeholder="车辆品牌" clearable="clearable">
<el-option v-for="c in brands" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="车辆型号">
<el-select v-model="formInline.modelId" placeholder="车辆型号" clearable="clearable">
<el-option v-for="c in models" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
<el-button type="primary" @click="openAdd">添加</el-button>
</el-form-item>
</el-form>
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column prop="num" label="车辆编号" width="180"></el-table-column>
<el-table-column prop="modelName" label="型号" width="100"></el-table-column>
<el-table-column prop="brandName" label="品牌" width="100"></el-table-column>
<el-table-column prop="outs" label="排量(T)" width="100">
<template v-slot="scope">
{{scope.row.outs}}.0
</template>
</el-table-column>
<el-table-column prop="speed" label="变速箱" width="100"></el-table-column>
<el-table-column prop="years" label="年代" width="100"></el-table-column>
<el-table-column prop="rent" label="日租金" width="100"></el-table-column>
<el-table-column prop="imgUrl" label="图片" width="150">
<template v-slot="scope">
<el-image :src="scope.row.imgUrl" style="width: 80px;height: 80px"></el-image>
</template>
</el-table-column>
<el-table-column prop="detail" label="介绍" width="180"></el-table-column>
<el-table-column prop="stat" label="状态" width="100">
<template v-slot="scope">
<span v-if="scope.row.stat==0" style="color: green">空闲</span>
<span v-if="scope.row.stat==1" style="color: orange">出租</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template v-slot="scope">
<el-button type="warning" size="mini" @click="openUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="delCar(scope.row.id)">删除</el-button>
<span v-if="scope.row.stat==0">
<el-button type="primary" size="mini" @click="goCar(scope.row)">租车</el-button>
</span>
</template>
</el-table-column>
</el-table>
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="current"
:page-sizes="[3, 6, 9, 12]"
:page-size="size"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
<el-dialog title="车辆录入" :visible.sync="addDialogFormVisible">
<el-form :model="addForm">
<el-form-item label="车辆信息:" :label-width="formLabelWidth">
<el-select v-model="addForm.brandId" placeholder="请选择车辆品牌" clearable="clearable">
<el-option v-for="c in brands" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
<el-select v-model="addForm.distId" placeholder="请选择车系" clearable="clearable">
<el-option v-for="c in dists" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
<el-select v-model="addForm.modelId" placeholder="请选择车辆型号" clearable="clearable">
<el-option v-for="c in models" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="预售价格:" :label-width="formLabelWidth">
<el-input v-model="addForm.price" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="行驶里程:" :label-width="formLabelWidth">
<el-input v-model="addForm.mileage" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="车辆颜色:" :label-width="formLabelWidth">
<el-radio-group v-model="addForm.colorId">
<el-radio v-for="c in colors" :label="c.id" >{{c.distValue}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="联系人:" :label-width="formLabelWidth">
<el-input v-model="addForm.userName" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="手机号:" :label-width="formLabelWidth">
<el-input v-model="addForm.userPhone" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="车辆介绍:" :label-width="formLabelWidth">
<el-input type="textarea" v-model="addForm.detail" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="车辆图片:" :label-width="formLabelWidth">
<el-upload
class="avatar-uploader"
action="http://127.0.0.1:9999/car-api/upload/minio"
:show-file-list="false"
:on-success="handleAvatarSuccess">
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="addDialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="saveCar('addForm')">确 定</el-button>
</div>
</el-dialog>
<!--车辆修改-->
<el-dialog title="车辆修改" :visible.sync="updateDialogFormVisible">
<el-form :model="updateForm">
<el-form-item label="车辆信息:" :label-width="formLabelWidth">
<el-select v-model="updateForm.brandId" placeholder="请选择车辆品牌" clearable="clearable">
<el-option v-for="c in brands" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
<el-select v-model="updateForm.distId" placeholder="请选择车系" clearable="clearable">
<el-option v-for="c in dists" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
<el-select v-model="updateForm.modelId" placeholder="请选择车辆型号" clearable="clearable">
<el-option v-for="c in models" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="预售价格:" :label-width="formLabelWidth">
<el-input v-model="updateForm.price" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="行驶里程:" :label-width="formLabelWidth">
<el-input v-model="updateForm.mileage" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="车辆颜色:" :label-width="formLabelWidth">
<el-radio-group v-model="updateForm.colorId">
<el-radio v-for="c in colors" :label="c.id" >{{c.distValue}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="联系人:" :label-width="formLabelWidth">
<el-input v-model="updateForm.userName" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="手机号:" :label-width="formLabelWidth">
<el-input v-model="updateForm.userPhone" autocomplete="off" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="车辆介绍:" :label-width="formLabelWidth">
<el-input type="textarea" v-model="updateForm.detail" clearable="clearable"></el-input>
</el-form-item>
<el-form-item label="车辆图片:" :label-width="formLabelWidth">
<el-upload
class="avatar-uploader"
action="http://127.0.0.1:9999/car-api/upload/minio"
:show-file-list="false"
:on-success="handleAvatarSuccess">
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="updateDialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="updateCar('updateForm')">确 定</el-button>
</div>
</el-dialog>
<el-dialog title="租车详情" :visible.sync="orderDialogFormVisible">
<el-form :model="orderForm">
<el-form-item label="车辆编号" :label-width="formLabelWidth">
<el-input v-model="orderForm.carNo" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="租金/天" :label-width="formLabelWidth">
<el-input v-model="orderForm.carRent" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="租用天数" :label-width="formLabelWidth">
<el-date-picker
v-model="value"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="orderDialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="saveOrder">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
- 订单列表,OrderView.vue
<script>
export default {
data(){
return{
tableData:[],
}
},
methods:{
check(){
this.axios.get("/car-api/pay/check").then(res=>{
if(res.data.flag){
this.$message.success(res.data.msg);
this.initList();
}else {
this.$message.error(res.data.msg);
}
})
},
refund(no){
this.axios.get("/car-api/pay/refund?no="+no).then(res=>{
if(res.data.flag){
this.$message.success(res.data.msg);
this.initList();
}else {
this.$message.error(res.data.msg);
}
})
},
goPay(no){
window.location.href="http://localhost:9999/car-api/pay/goPay?no="+no;
},
initList(){
this.axios.get("/car-api/order/list").then(res=>{
this.tableData = res.data;
})
},
},
created() {
this.initList();
}
}
</script>
<template>
<div>
<el-button type="primary" @click="check">校验订单</el-button>
<el-image src="https://llh-images.oss-cn-beijing.aliyuncs.com/test/6454bb7c-55e7-4ae7-808c-5c6c5204f0e2.jpg" style="width: 100px;height: 100px"></el-image>
<el-image src="https://llh-images.oss-cn-beijing.aliyuncs.com/bdaffc4d-cdac-46dd-85a4-ea36cecb122b.jpg" style="width: 100px;height: 100px"></el-image>
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column prop="num" label="订单编号" width="180"></el-table-column>
<el-table-column prop="carNo" label="车辆编号" width="180"></el-table-column>
<el-table-column prop="carRent" label="租金/天" width="150"></el-table-column>
<el-table-column prop="carPrice" label="费用" width="150"></el-table-column>
<el-table-column prop="userPhone" label="联系人电话" width="150"></el-table-column>
<el-table-column prop="remark" label="说明" width="180">
<template v-slot="scope">
<span v-if="scope.row.remark=='已支付,状态码未改'" style="color: orange">
{{scope.row.remark}}
<el-button type="warning" size="mini" @click="goUpdate(scope.row.num)">去修改</el-button>
</span>
<span v-if="scope.row.remark=='未支付,状态码已改'" style="color: orange">
{{scope.row.remark}}
<el-button type="warning" size="mini" @click="goPay(scope.row.num)">去支付</el-button>
</span>
<span v-if="scope.row.remark=='已支付'" style="color: green">
{{scope.row.remark}}
</span>
<span v-if="scope.row.remark=='已退款'" style="color: red">
{{scope.row.remark}}
</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="提交时间" width="180"></el-table-column>
<el-table-column label="操作" width="150">
<template v-slot="scope">
<span v-if="scope.row.stat==0">
<el-button type="primary" size="mini" @click="goPay(scope.row.num)">待支付</el-button>
</span>
<span v-if="scope.row.stat==1">
<el-button type="danger" size="mini" @click="refund(scope.row.num)">去退款</el-button>
</span>
<span v-if="scope.row.stat==2" style="color: red">
已退款
</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<style scoped>
</style>
93 comments
作者对主题的挖掘深入骨髓,展现了非凡的洞察力和理解力。
文章的叙述风格独特,用词精准,让人回味无穷。
作者的观点新颖且实用,让人在阅读中获得了新的思考和灵感。
建议增加田野调查素材,提升真实性。
若能在案例选择上更贴近现实,说服力会进一步提升。
思想的火花在字句间迸发,照亮认知盲区。
?内容类评语?
建议提出分阶段实施路径,增强可行性。
作者以简洁明了的语言,传达了深刻的思想和情感。
作者的布局谋篇匠心独运,让读者在阅读中享受到了思维的乐趣。
作者以简洁明了的语言,传达了深刻的思想和情感。
立意高远,以小见大,引发读者对社会/人性的深层共鸣。
逆境中的反思充满生命韧性。
社会责任感贯穿全文,彰显学者担当。
哈哈哈,写的太好了https://www.lawjida.com/
哈哈哈,写的太好了https://www.lawjida.com/
《新精武门1991粤语》喜剧片高清在线免费观看:https://www.jgz518.com/xingkong/17764.html
《杜心五传奇》国产剧高清在线免费观看:https://www.jgz518.com/xingkong/116626.html
《难以置信的怪物》恐怖片高清在线免费观看:https://www.jgz518.com/xingkong/102034.html
你的才华横溢,让人敬佩。 http://www.55baobei.com/95XEL6IorB.html