基于javaweb+mysql的ssm+maven网上图书商城系统(java+ssm+jsp+mysql+redis+easyui)
运行环境
Java≥8、MySQL≥5.7、Tomcat≥8
开发工具
eclipse/idea/myeclipse/sts等均可配置运行
适用
课程设计,大作业,毕业设计,项目练习,学习演示等
功能说明
基于javaweb+mysql的SSM+Maven网上图书商城系统(java+ssm+jsp+mysql+redis+easyui)
这个项目涉及到Shiro整合JWT、秒杀功能所具备的基本要求(限流、乐观锁、接口隐藏、JMeter高并发测试等等)、消息中间件RabbitMQ的异步邮件通知和死信队列、沙箱支付宝模拟支付等等技术亮点。
项目功能:该项目分为买家、卖家、管理员三个角色。买家角色的功能:登录、注册、浏览图书、管理购物车、结算订单、支付订单、查看订单、修改个人信息等等功能。
卖家角色的功能:登录、注册、浏览商品、管理自己发布的图书、管理收到的订单、查看自己的收益详情等等。管理员角色的功能:登录、管理所有用户信息、管理权限信息、管理所有卖家发布的图书、管理所有订单信息、管理所有支付信息、查看总共收益详情等等。
应用技术:SSM + Jsp + MySQL + Redis + JWT + Shiro + EasyUI等
运行环境:Eclipse/IntelliJ IDEA + MySQL5.7 + Maven3.6.3+ JDK1.8 + Redis5.0.5+ Tomcat8.5 +
*/
/**
* 前台订单Order控制类
*/
@Controller
public class HomeOrderController {
@Autowired
private IOrderService orderService;
/**
* 前往订单结算页面
* @return
*/
@GetMapping("/home/order/index")
public String index(){
return "home/order/index";
}
/**
* 前往订单结算页面前数据处理
* @param ids
* @param request
* @return
*/
@PostMapping("/home/order/index")
@ResponseBody
public ResponseVo<Boolean> index(String ids, HttpServletRequest request){
return orderService.toOrderList(ids, request);
}
/**
* 获取订单数据
* @param request
* @return
*/
@PostMapping("/home/order/data")
@ResponseBody
public ResponseVo<Order> data(HttpServletRequest request){
private Logger log = LoggerFactory.getLogger(CaptchaController.class);
/**
* 通用验证码生成器
* @param vcodeLength
* @param fontSize
* @param width
* @param height
* @param method
* @param request
* @param response
*/
@GetMapping(value="/common/captcha/generate_captcha")
public void generateCaptcha(
@RequestParam(name="vl",defaultValue="4")Integer vcodeLength,//vcodeLength,验证码长度
@RequestParam(name="fs",defaultValue="21")Integer fontSize,//fontSize,验证码字体大小
@RequestParam(name="w",defaultValue="98")Integer width,//width,图片宽度
@RequestParam(name="h",defaultValue="33")Integer height,//height,图片高度
@RequestParam(name="method")String method,//用来调用此方法的名称,以此名称为键,存入到session中
HttpServletRequest request,
HttpServletResponse response){
CaptchaUtil captchaUtil = new CaptchaUtil(vcodeLength,fontSize,width,height);
String generatorVCode = captchaUtil.generatorVCode(); //验证码的值
//将生成的验证码放入session,以供放后面程序的验证使用
request.getSession().setAttribute(method, generatorVCode);
log.info("验证码成功生成,method=" + method + ",value=" + generatorVCode);
try {
ImageIO.write(captchaUtil.generatorRotateVCodeImage(generatorVCode, true), "gif", response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 后台用户列表页面
* @return
*/
@GetMapping("/admin/user/list")
public String list(){
return "admin/user/list";
}
/**
* 后台用户列表信息获取
* @param page
* @param rows
* @param roleId
* @param username
* @return
*/
@PostMapping("/admin/user/list")
@ResponseBody
public Map<String, Object> list(Integer page, Integer rows, Integer roleId, String username){
return userService.getUserList(page, rows, roleId, username);
}
/**
* 后台添加用户信息操作处理
* @param user
* @return
*/
@PostMapping("/admin/user/add")
@ResponseBody
public ResponseVo<Boolean> add(User user){
return userService.addUser(user);
}
/**
* 后台修改用户信息操作处理
* @param user
* @return
*/
@PostMapping("/admin/user/edit")
@ResponseBody
public ResponseVo<Boolean> edit(User user){
return userService.editUser(user);
}
/**
* 后台删除用户信息操作处理
* @param ids
* @return
public Map<String, Object> list(Page page, String customerName, String sellerName, Integer state, String orderNo){
return orderService.getOrderList(page, customerName, sellerName, state, orderNo);
}
/**
* 删除订单操作处理
* @param ids
* @return
*/
@PostMapping("/admin/order/delete")
@ResponseBody
public ResponseVo<Boolean> delete(String ids){
return orderService.deleteOrder(ids);
}
/**
* 修改订单状态操作处理
* @param order
* @return
*/
@PostMapping("/admin/order/edit_state")
@ResponseBody
public ResponseVo<Boolean> editState(Order order){
return orderService.editState(order);
}
}
/**
*/
/**
* @return
*/
@GetMapping("/admin/authority/list")
public String list(){
return "admin/authority/list";
}
/**
* 获取权限列表信息
* @param page
* @param rows
* @param roleId
* @param name
* @return
*/
@PostMapping("/admin/authority/list")
@ResponseBody
public Map<String, Object> list(Integer page, Integer rows, Integer roleId, String name){
return authorityService.getAuthorityList(page, rows, roleId, name);
}
/**
* 添加权限信息操作处理
* @param authority
* @return
*/
@PostMapping("/admin/authority/add")
@ResponseBody
public ResponseVo<Boolean> add(Authority authority){
return authorityService.addAuthority(authority);
}
/**
* 修改权限信息操作处理
* @param authority
* @return
*/
@PostMapping("/admin/authority/edit")
@ResponseBody
public ResponseVo<Boolean> edit(Authority authority){
return authorityService.editAuthority(authority);
}
/**
* 修改权限信息操作处理
* @param ids
* @return
*/
@PostMapping("/admin/authority/delete")
@ResponseBody
// //商户订单号,商户网站订单系统中唯一订单号,必填
// //生成随机Id
// String out_trade_no = selectedPay.getPayNo();
// //付款金额,必填
// String total_amount = String.valueOf(selectedPay.getTotalAmount());
// //订单名称,必填
// String subject ="网上图书商城订单支付";
// //商品描述,可空
// String body = "尊敬的用户:"+user.getUsername()+",欢迎您在网上图书商城下单!";
// request.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\""+"5m"+"\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
// String form = "";
// try {
// form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
// } catch (AlipayApiException e) {
// e.printStackTrace();
// }
// httpResponse.setContentType("text/html;charset=" + CHARSET);
// httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
// httpResponse.getWriter().flush();
// httpResponse.getWriter().close();
// }
//
//
// /**
// * 继续支付页面
// * @param orderId
// * @param req
// * @param httpResponse
// * @throws IOException
// */
// @GetMapping("/common/alipay/continue")
// public void continuePay(String orderId, HttpServletRequest req, HttpServletResponse httpResponse) throws IOException {
// User user = (User) SecurityUtils.getSubject().getPrincipal();
// if(user == null){
// throw new RuntimeException("还未登录或会话失效,请重新登录!");
// }
// if(CommonUtil.isEmpty(orderId)){
// throw new RuntimeException("数据非法,支付失败,请重新操作!");
// }
// QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
// orderQueryWrapper.eq("customer_id", user.getId());
// orderQueryWrapper.eq("id", orderId);
}
/**
* 上架图书操作处理
* @param id
* @return
*/
@PostMapping("/admin/book/on_shelves")
@ResponseBody
public ResponseVo<Boolean> onShelves(String id){
return bookService.onShelves(id);
}
/**
* 修改图书状态操作处理
* @param id
* @param state
* @return
*/
@PostMapping("/admin/book/edit_state")
@ResponseBody
public ResponseVo<Boolean> editState(String id, Integer state){
return bookService.editState(id, state);
}
}
/**
*/
/**
* 后台用户User控制类
*/
@Controller
public class UserController {
}
return false;
}
//不是ajax请求,直接跳转页面
try {
log.info("没有登录或token非法,跳转登录界面!当前URL={}",requestURI);
//根据路径分别跳转不同的登录页面
if(requestURI.contains("/home/")){
response.sendRedirect("../system/index");
}else if(requestURI.contains("/admin/")){
response.sendRedirect("../user/login");
}else{
response.sendRedirect("../../common/system/not_permit");
}
} catch (IOException e3) {
e3.printStackTrace();
}
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
/**
*/
/**
* 后台订单Order控制类
*/
@Controller
public class OrderController {
@Autowired
private IOrderService orderService;
/**
* 订单列表页面
* @return
*/
@GetMapping("/admin/order/list")
public String list(){
return "admin/order/list";
}
/**
* 近期订单收益图表页面
* @return
*/
@GetMapping("/admin/order/stats")
public String stats(){
return "admin/order/stats";
}
/**
if(stockDao.updateStock(cart.getQuantity(), stock) <= 0){
throw new RuntimeException("提交订单失败,请稍后重试!");
}
}
// 6.创建订单 6.1同一个卖家的商品归并到一个订单中 Map<> 键key:卖家id 值value:购物车列表
Map<String, List<Cart>> orderItemMap = new HashMap<>();
cartList.forEach(e->{
if(orderItemMap.containsKey(String.valueOf(e.getBook().getSellerId()))){
//如果key中存在某卖家id
List<Cart> selectedCartList = orderItemMap.get(String.valueOf(e.getBook().getSellerId()));
selectedCartList.add(e);
orderItemMap.put(String.valueOf(e.getBook().getSellerId()), selectedCartList);
}else{
//如果key中不存在某卖家id
List<Cart> selectedCartList = new ArrayList<>();
selectedCartList.add(e);
orderItemMap.put(String.valueOf(e.getBook().getSellerId()), selectedCartList);
}
});
Set<Long> orderIdSet = new HashSet<>(); //储存订单id的Set集合
BigDecimal payTotalAmount = new BigDecimal("0.00"); //计算一个用户本次购买的总金额
// 6.创建订单 6.2给每个订单赋值,准备存入数据库
for(Map.Entry<String, List<Cart>> entry : orderItemMap.entrySet()){
BigDecimal totalPrice = new BigDecimal("0.00");
List<OrderItem> orderItemList = new ArrayList<>();
List<Cart> selectedCartList = entry.getValue(); //值
//计算总价,并封装订单详情数据
for(Cart cart : selectedCartList){
OrderItem orderItem = new OrderItem();
if(BookIsKillEnum.YES.getCode().equals(cart.getBook().getIsKill())){
BigDecimal subtotal = new BigDecimal(cart.getQuantity()).multiply(cart.getBook().getNewPrice());
orderItem.setSubtotal(subtotal);
orderItem.setBookPrice(cart.getBook().getNewPrice());
totalPrice = totalPrice.add(subtotal);
} else if(BookIsKillEnum.NO.getCode().equals(cart.getBook().getIsKill())){
BigDecimal subtotal = new BigDecimal(cart.getQuantity()).multiply(cart.getBook().getOldPrice());
orderItem.setSubtotal(subtotal);
orderItem.setBookPrice(cart.getBook().getOldPrice());
totalPrice = totalPrice.add(subtotal);
}
orderItem.setQuantity(cart.getQuantity());
orderItem.setBookId(cart.getBook().getId());
orderItem.setBookName(cart.getBook().getName());
orderItem.setBookPic(cart.getBook().getPhoto());
orderItemList.add(orderItem);
}
order.setOrderNo(String.valueOf(new SnowFlake(2,3).nextId()));
order.setTotalPrice(totalPrice);
payTotalAmount = payTotalAmount.add(totalPrice);
order.setCustomerId(user.getId());
order.setSellerId(Long.valueOf(entry.getKey()));
}
// 2.判断图书的状态是否是审核通过
if(!BookStateEnum.AUDIT_SUCCESS.getCode().equals(cart.getBook().getState())){
//图书状态不是审核通过,自定义错误消息返回
throw new RuntimeException("图书《"+cart.getBook().getName()+"》的状态异常,不允许购买了哦!");
}
// 3.判断是否是秒杀图书。若是,则是否已过秒杀时间
if(bookService.checkKillBook(cart.getBookId(), new Date())){
//是秒杀图书并且已过秒杀时间 自定义错误消息返回
throw new RuntimeException("限时秒杀图书《"+cart.getBook().getName()+"》的秒杀时间已过,不允许购买了哦!");
}
// 4.库存检查
Stock stock = stockService.checkStock(cart.getBookId(), cart.getQuantity());
if(stock == null){
//库存检查不合格,自定义错误消息返回
throw new RuntimeException("图书《"+cart.getBook().getName()+"》已售罄,不允许购买了哦!");
}
// 5.更新库存 使用乐观锁版本号控制 避免超卖现象出现
if(stockDao.updateStock(cart.getQuantity(), stock) <= 0){
throw new RuntimeException("提交订单失败,请稍后重试!");
}
}
// 6.创建订单 6.1同一个卖家的商品归并到一个订单中 Map<> 键key:卖家id 值value:购物车列表
Map<String, List<Cart>> orderItemMap = new HashMap<>();
cartList.forEach(e->{
if(orderItemMap.containsKey(String.valueOf(e.getBook().getSellerId()))){
//如果key中存在某卖家id
List<Cart> selectedCartList = orderItemMap.get(String.valueOf(e.getBook().getSellerId()));
selectedCartList.add(e);
orderItemMap.put(String.valueOf(e.getBook().getSellerId()), selectedCartList);
}else{
//如果key中不存在某卖家id
List<Cart> selectedCartList = new ArrayList<>();
selectedCartList.add(e);
orderItemMap.put(String.valueOf(e.getBook().getSellerId()), selectedCartList);
}
});
Set<Long> orderIdSet = new HashSet<>(); //储存订单id的Set集合
BigDecimal payTotalAmount = new BigDecimal("0.00"); //计算一个用户本次购买的总金额
// 6.创建订单 6.2给每个订单赋值,准备存入数据库
for(Map.Entry<String, List<Cart>> entry : orderItemMap.entrySet()){
BigDecimal totalPrice = new BigDecimal("0.00");
List<OrderItem> orderItemList = new ArrayList<>();
List<Cart> selectedCartList = entry.getValue(); //值
//计算总价,并封装订单详情数据
for(Cart cart : selectedCartList){
OrderItem orderItem = new OrderItem();
if(BookIsKillEnum.YES.getCode().equals(cart.getBook().getIsKill())){
BigDecimal subtotal = new BigDecimal(cart.getQuantity()).multiply(cart.getBook().getNewPrice());
orderItem.setSubtotal(subtotal);
orderItem.setBookPrice(cart.getBook().getNewPrice());
/**
*/
/**
* 后台订单Order控制类
*/
@Controller
public class OrderController {
@Autowired
private IOrderService orderService;
/**
* 订单列表页面
* @return
*/
@GetMapping("/admin/order/list")
public String list(){
return "admin/order/list";
}
/**
* 近期订单收益图表页面
* @return
*/
@GetMapping("/admin/order/stats")
public String stats(){
return "admin/order/stats";
}
/**
* 获取订单收益图表的数据
* @return
*/
@PostMapping("/admin/order/stats")
@ResponseBody
public Map<String, Object> getStats(){
return orderService.getStatsData();
}
/**
* 获取订单数据操作处理
* @param page
* @param customerName
* @param sellerName
* @param state
}
//不是ajax请求,直接跳转页面
try {
log.info("没有登录或token非法,跳转登录界面!当前URL={}",requestURI);
//根据路径分别跳转不同的登录页面
if(requestURI.contains("/home/")){
response.sendRedirect("../system/index");
}else if(requestURI.contains("/admin/")){
response.sendRedirect("../user/login");
}else{
response.sendRedirect("../../common/system/not_permit");
}
} catch (IOException e3) {
e3.printStackTrace();
}
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
if(payItemList == null || payItemList.size() == 0){
throw new RuntimeException("支付成功,但订单状态异常,下单失败,请及时联系管理员解决!");
}
payItemList.forEach(e->{orderIdSet.add(e.getOrderId());});
List<Order> orderList = orderDao.selectBatchIds(orderIdSet);
if(orderList == null || orderList.size() == 0){
throw new RuntimeException("支付成功,但订单状态异常,下单失败,请及时联系管理员解决!");
}
for(Order order : orderList){
order.setState(OrderStateEnum.PAYED.getCode());
if(orderDao.updateById(order) <= 0){
throw new RuntimeException("支付成功,但订单状态异常,下单失败,请及时联系管理员解决!");
}
}
return orderList;
}
@Override
public ResponseVo<Map<String, Object>> getUserOrderData(Page page) {
Map<String, Object> ret = new HashMap<>();
User user = (User) SecurityUtils.getSubject().getPrincipal();
if(user == null){
return ResponseVo.errorByMsg(CodeMsg.USER_SESSION_EXPIRED);
}
Map<String, Object> queryMap = new HashMap<>();
if(page != null){
page.setRows(1); //每页1条数据
queryMap.put("offset", page.getOffset());
queryMap.put("pageSize", page.getRows());
}
queryMap.put("customerId", user.getId());
queryMap.put("isDeleted", OrderDeleteEnum.NO.getCode());
List<Order> orderList = orderDao.selectAll(queryMap);
ret.put("orderList", orderList);
ret.put("page", page.getPage());
ret.put("totalCount", orderDao.getAllTotal(queryMap));
page.setTotalCount(orderDao.getAllTotal(queryMap));
ret.put("totalPage", page.getTotalPage());
ret.put("sendTotal", orderDao.getCountByOrderState(user.getId(), OrderStateEnum.SEND.getCode()));
ret.put("signTotal", orderDao.getCountByOrderState(user.getId(), OrderStateEnum.SIGN.getCode()));
return ResponseVo.success(ret);
}
@Override
public ResponseVo<Boolean> delOrderByUser(String orderId) {
User user = (User) SecurityUtils.getSubject().getPrincipal();
if(user == null){
return ResponseVo.errorByMsg(CodeMsg.USER_SESSION_EXPIRED);
}
@PostMapping("/admin/order/list")
@ResponseBody
public Map<String, Object> list(Page page, String customerName, String sellerName, Integer state, String orderNo){
return orderService.getOrderList(page, customerName, sellerName, state, orderNo);
}
/**
* 删除订单操作处理
* @param ids
* @return
*/
@PostMapping("/admin/order/delete")
@ResponseBody
public ResponseVo<Boolean> delete(String ids){
return orderService.deleteOrder(ids);
}
/**
* 修改订单状态操作处理
* @param order
* @return
*/
@PostMapping("/admin/order/edit_state")
@ResponseBody
public ResponseVo<Boolean> editState(Order order){
return orderService.editState(order);
}
}
/**
if(requestURI.contains("/admin/")){
DecodedJWT decodedJWT = JWTUtil.verifyToken(adminToken);
request.setAttribute("id", decodedJWT.getClaim("id").asString());
request.setAttribute("username", decodedJWT.getClaim("username").asString());
request.setAttribute("phone", decodedJWT.getClaim("phone").asString());
request.setAttribute("email", decodedJWT.getClaim("email").asString());
request.setAttribute("roleId", decodedJWT.getClaim("roleId").asString());
//如果token合法,提交给Shiro
Subject subject = SecurityUtils.getSubject();
subject.getSession().setTimeout(604800000); //设置session过期时间:604800000ms = 7天
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(decodedJWT.getClaim("username").asString(), decodedJWT.getClaim("password").asString());
subject.login(usernamePasswordToken);
}
}catch(Exception e){
//e.printStackTrace();
//token失效或非法
//判断该路径是否需要验证
for(String str : RuntimeConstant.userNotNeedConfirmUrl){
if(requestURI.equals(str)){
return true; //该路径不需要验证,直接通行
}
}
//判断是否是ajax请求或者是axios请求
if (CommonUtil.isAjax(request) || CommonUtil.isAxios(request)) {
//表示是ajax请求或者是axios请求
try {
response.setCharacterEncoding("UTF-8");
//JSON.parseObject,是将Json字符串转化为相应的对象;JSON.toJSONString则是将对象转化为Json字符串。
response.getWriter().write(JSON.toJSONString(CodeMsg.USER_SESSION_EXPIRED));
} catch (IOException e2) {
e2.printStackTrace();
}
return false;
}
//不是ajax请求,直接跳转页面
try {
log.info("没有登录或token非法,跳转登录界面!当前URL={}",requestURI);
//根据路径分别跳转不同的登录页面
if(requestURI.contains("/home/")){
response.sendRedirect("../system/index");
}else if(requestURI.contains("/admin/")){
response.sendRedirect("../user/login");
}else{
response.sendRedirect("../../common/system/not_permit");
* 获取用户我的订单的数据
* @param page
* @return
*/
@PostMapping("/home/user/my_order")
@ResponseBody
public ResponseVo<Map<String, Object>> getMyOrderData(@RequestBody Page page){
return orderService.getUserOrderData(page);
}
/**
* 用户删除订单操作处理
* @param orderId
* @return
*/
@PostMapping("/home/user/del_order")
@ResponseBody
public ResponseVo<Boolean> delOrderByUser(String orderId){
return orderService.delOrderByUser(orderId);
}
}
/**
*/
/**
* 运行时触发异常捕获
*/
@ControllerAdvice
public class RuntimeExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(RuntimeExceptionHandler.class);
@ExceptionHandler(RuntimeException.class)