diff --git a/sky/sky-server/src/main/java/com/sky/config/RedisConfiguration.java b/sky/sky-server/src/main/java/com/sky/config/RedisConfiguration.java new file mode 100644 index 0000000..10dd9af --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/config/RedisConfiguration.java @@ -0,0 +1,35 @@ +// 导入所需的包 +package com.sky.config; + +// 导入Lombok提供的@Slf4j注解,用于自动注入一个日志对象 +import lombok.extern.slf4j.Slf4j; +// 导入Spring框架的配置注解,用于声明这是一个配置类 +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +// 导入Spring Data Redis的连接工厂接口 +import org.springframework.data.redis.connection.RedisConnectionFactory; +// 导入Spring Data Redis的核心组件,用于操作Redis +import org.springframework.data.redis.core.RedisTemplate; +// 导入Spring Data Redis的字符串序列化器 +import org.springframework.data.redis.serializer.StringRedisSerializer; + +// 使用@Configuration注解,声明这个类是一个Spring配置类,其中的Bean会被Spring容器自动扫描和管理。 +@Configuration +// 使用@Slf4j注解,自动为这个类注入一个日志对象,方便记录日志信息。 +@Slf4j +public class RedisConfiguration { + // 使用@Bean注解,声明一个Bean,Spring容器会调用这个方法来创建Bean,并将其加入到容器中。 + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + // 记录日志信息,表示开始创建Redis模板对象。 + log.info("开始创建redis模版对象..."); + // 创建RedisTemplate实例,泛型参数为Object,表示键和值都可以是任意类型的对象。 + RedisTemplate redisTemplate = new RedisTemplate<>(); + // 设置连接工厂对象,这个对象负责与Redis服务器建立连接。 + redisTemplate.setConnectionFactory(redisConnectionFactory); + // 设置Redis key的序列化器,这里使用StringRedisSerializer,表示key会被序列化为String类型。 + redisTemplate.setKeySerializer(new StringRedisSerializer()); + // 返回配置好的RedisTemplate对象,它将被Spring容器管理。 + return redisTemplate; + } +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/controller/admin/CommonController.java b/sky/sky-server/src/main/java/com/sky/controller/admin/CommonController.java new file mode 100644 index 0000000..4b7320b --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/controller/admin/CommonController.java @@ -0,0 +1,68 @@ +// 导入所需的包 +package com.sky.controller.admin; + +// 导入项目中定义的消息常量类 +import com.sky.constant.MessageConstant; +// 导入项目中定义的结果封装类 +import com.sky.result.Result; +// 导入项目中定义的通用服务接口 +import com.sky.service.CommonService; +// 导入Swagger注解,用于生成API文档 +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +// 导入Lombok提供的@Slf4j注解,用于自动注入一个日志对象 +import lombok.extern.slf4j.Slf4j; +// 导入Spring框架的自动注入注解 +import org.springframework.beans.factory.annotation.Autowired; +// 导入Spring框架的映射注解 +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +// 导入Spring框架的文件上传组件 +import org.springframework.web.multipart.MultipartFile; + +// 导入Java IO异常类 +import java.io.IOException; + +// 使用@RestController注解,声明这个类是一个Spring MVC的控制器,并且返回的数据会自动以JSON格式响应。 +@RestController +// 使用@RequestMapping注解,声明这个控制器处理请求的基本路径。 +@RequestMapping("/admin/common") +// 使用@Api注解,指定这个控制器是Swagger文档中的一个API,并提供API的描述。 +@Api("通用接口") +// 使用@Slf4j注解,自动为这个类注入一个日志对象,方便记录日志信息。 +@Slf4j +public class CommonController { + + // 使用@Autowired注解,自动注入CommonService服务。 + @Autowired + // 定义CommonService类型的变量commmonService,注意这里有一个拼写错误,应该是commonService。 + CommonService commmonService; + + /** + * 文件上传的处理方法。 + * + * @param file 前端传过来的文件,使用MultipartFile类型表示。 + * @return 返回一个Result对象,其中包含操作结果和消息。 + */ + // 使用@ApiOperation注解,为Swagger文档提供文件上传操作的描述。 + @ApiOperation("文件上传") + // 使用@PostMapping注解,指定这个方法处理POST请求。 + @PostMapping("/upload") + public Result upload(MultipartFile file) { + // 记录日志信息,包括文件上传的操作和传入的文件对象。 + log.info("文件上传{}", file); + // 定义一个String类型的变量filePath,用于存储文件上传后的路径。 + String filePath; + try { + // 调用commmonService的upload方法,上传文件,并返回文件路径。 + filePath = commmonService.upload(file); + } catch (IOException e) { + // 如果发生IOException异常,返回错误结果,使用MessageConstant中的UPLOAD_FAILED常量作为错误消息。 + return Result.error(MessageConstant.UPLOAD_FAILED); + } + + // 如果文件上传成功,返回成功结果,包含文件路径。 + return Result.success(filePath); + } +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/controller/admin/ReportController.java b/sky/sky-server/src/main/java/com/sky/controller/admin/ReportController.java new file mode 100644 index 0000000..a404bd2 --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/controller/admin/ReportController.java @@ -0,0 +1,134 @@ +// 定义了一个名为com.sky.controller.admin的包,用于存放控制器类 +package com.sky.controller.admin; + +// 导入了项目中定义的结果类 +import com.sky.result.Result; +// 导入了项目中定义的报告服务接口 +import com.sky.service.ReportService; +// 导入了项目中定义的视图对象类 +import com.sky.vo.*; +// 导入了Swagger注解,用于API文档的生成 +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +// 导入了Lombok提供的@Slf4j注解,用于简化日志操作 +import lombok.extern.slf4j.Slf4j; +// 导入了HttpServletResponse类,用于处理HTTP响应 +import javax.servlet.http.HttpServletResponse; +// 导入了IOException类,用于处理可能发生的IO异常 +import java.io.IOException; +// 导入了LocalDate类,用于处理日期 +import java.time.LocalDate; +// 导入了InvalidFormatException类,用于处理可能发生格式无效的异常 +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; + +/** + * 报告控制器,提供营业额、用户统计、订单统计、销量排行等报告接口服务。 + */ +// 使用@RestController注解声明这是一个控制器类,并且返回的数据会自动以JSON格式响应 +@RestController +// 使用@RequestMapping注解定义这个控制器类的基础请求映射路径 +@RequestMapping("/admin/report") +// 使用@Slf4j注解提供日志功能 +@Slf4j +// 使用@Api注解为这个控制器类添加Swagger文档的描述 +@Api("营业额相关接口") +public class ReportController { + + // 自动注入ReportService + @Autowired + ReportService reportService; + + /** + * 营业额统计接口。 + * + * @param begin 开始日期 + * @param end 结束日期 + * @return 返回营业额统计的结果 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("营业额相关接口") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/turnoverStatistics") + public Result turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) { + // 使用日志记录营业额统计的操作 + log.info("营业额统计:{},{}", begin, end); + // 调用ReportService的getTurnoverStatistics方法获取营业额统计结果 + return reportService.getTurnoverStatistics(begin, end); + } + + /** + * 用户统计接口。 + * + * @param begin 开始日期 + * @param end 结束日期 + * @return 返回用户统计的结果 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("用户统计") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/userStatistics") + public Result userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) { + // 使用日志记录用户统计的操作 + log.info("用户统计:{},{}", begin, end); + // 调用ReportService的userStatistics方法获取用户统计结果,并包装成成功的响应 + return Result.success(reportService.userStatistics(begin, end)); + } + + /** + * 订单统计接口。 + * + * @param begin 开始日期 + * @param end 结束日期 + * @return 返回订单统计的结果 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("订单统计") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/ordersStatistics") + public Result orderStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) { + // 使用日志记录订单统计的操作 + log.info("订单统计{},{}", begin, end); + // 调用ReportService的orderStatistics方法获取订单统计结果,并包装成成功的响应 + return Result.success(reportService.orderStatistics(begin, end)); + } + + /** + * 销量排名前十接口。 + * + * @param begin 开始日期 + * @param end 结束日期 + * @return 返回销量排名前十的结果 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("销量排行前十") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/top10") + public Result salesTop10Report(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) { + // 使用日志记录销量排名前十的操作 + log.info("销售排名前10:{},{}", begin, end); + // 调用ReportService的salesTop10Report方法获取销量排名前十的结果,并包装成成功的响应 + return Result.success(reportService.salesTop10Report(begin, end)); + } + + /** + * 导出excel表格接口。 + * + * @param httpResponse HTTP响应对象 + * @return 返回操作结果 + * @throws IOException 如果发生IO异常 + * @throws InvalidFormatException 如果发生格式无效的异常 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("导出excel表格") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/export") + public Result export(HttpServletResponse httpResponse) throws IOException, InvalidFormatException { + // 使用日志记录导出Excel表格的操作 + log.info("导出Excel表格"); + // 调用ReportService的export方法导出Excel表格 + reportService.export(httpResponse); + // 返回操作成功的结果 + return Result.success("OK"); + } + +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/controller/notify/PayNotifyController.java b/sky/sky-server/src/main/java/com/sky/controller/notify/PayNotifyController.java new file mode 100644 index 0000000..1205ec4 --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/controller/notify/PayNotifyController.java @@ -0,0 +1,135 @@ +// 定义了一个名为com.sky.controller.notify的包,用于存放支付回调相关的控制器类 +package com.sky.controller.notify; + +// 导入了JSON处理相关的类 +import com.alibaba.druid.support.json.JSONUtils; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +// 导入了项目中定义的微信支付属性类 +import com.sky.properties.WeChatProperties; +// 导入了项目中定义的订单服务接口 +import com.sky.service.OrderService; +// 导入了微信支付加解密工具类 +import com.wechat.pay.contrib.apache.httpclient.util.AesUtil; +// 导入了Lombok提供的@Slf4j注解,用于简化日志操作 +import lombok.extern.slf4j.Slf4j; +// 导入了HTTP相关的类 +import org.apache.http.entity.ContentType; +import org.springframework.beans.factory.annotation.Autowired; +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.BufferedReader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; + +/** + * 支付回调相关接口,用于处理微信支付的异步通知。 + */ +// 使用@RestController注解声明这是一个控制器类,并且返回的数据会自动以JSON格式响应 +@RestController +// 使用@RequestMapping注解定义这个控制器类的基础请求映射路径 +@RequestMapping("/notify") +// 使用@Slf4j注解提供日志功能 +@Slf4j +public class PayNotifyController { + // 自动注入OrderService + @Autowired + private OrderService orderService; + // 自动注入WeChatProperties + @Autowired + private WeChatProperties weChatProperties; + + /** + * 支付成功回调接口。 + * + * @param request HTTP请求对象 + * @param response HTTP响应对象 + * @throws Exception 可能抛出的异常 + */ + @RequestMapping("/paySuccess") + public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { + // 读取数据 + String body = readData(request); + log.info("支付成功回调:{}", body); + + // 数据解密 + String plainText = decryptData(body); + log.info("解密后的文本:{}", plainText); + + JSONObject jsonObject = JSON.parseObject(plainText); + // 商户平台订单号 + String outTradeNo = jsonObject.getString("out_trade_no"); + // 微信支付交易号 + String transactionId = jsonObject.getString("transaction_id"); + + log.info("商户平台订单号:{}", outTradeNo); + log.info("微信支付交易号:{}", transactionId); + + // 业务处理,修改订单状态、来单提醒 + orderService.paySuccess(outTradeNo); + + // 给微信响应 + responseToWeixin(response); + } + + /** + * 读取数据方法,从HTTP请求中读取数据。 + * + * @param request HTTP请求对象 + * @return 读取到的数据字符串 + * @throws Exception 可能抛出的异常 + */ + private String readData(HttpServletRequest request) throws Exception { + BufferedReader reader = request.getReader(); + StringBuilder result = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + if (result.length() > 0) { + result.append("\n"); + } + result.append(line); + } + return result.toString(); + } + + /** + * 数据解密方法,用于解密微信支付回调的数据。 + * + * @param body 需要解密的数据字符串 + * @return 解密后的字符串 + * @throws Exception 可能抛出的异常 + */ + private String decryptData(String body) throws Exception { + JSONObject resultObject = JSON.parseObject(body); + JSONObject resource = resultObject.getJSONObject("resource"); + String ciphertext = resource.getString("ciphertext"); + String nonce = resource.getString("nonce"); + String associatedData = resource.getString("associated_data"); + + AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8)); + // 密文解密 + String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), + nonce.getBytes(StandardCharsets.UTF_8), + ciphertext); + + return plainText; + } + + /** + * 给微信响应方法,用于向微信服务器返回处理结果。 + * + * @param response HTTP响应对象 + * @throws Exception 可能抛出的异常 + */ + private void responseToWeixin(HttpServletResponse response) throws Exception{ + response.setStatus(200); + HashMap map = new HashMap<>(); + map.put("code", "SUCCESS"); + map.put("message", "SUCCESS"); + response.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes(StandardCharsets.UTF_8)); + response.flushBuffer(); + } +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/controller/user/OrderController.java b/sky/sky-server/src/main/java/com/sky/controller/user/OrderController.java new file mode 100644 index 0000000..6f7e33e --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/controller/user/OrderController.java @@ -0,0 +1,159 @@ +// 定义了一个名为com.sky.controller.user的包,用于存放用户端订单控制器类 +package com.sky.controller.user; + +// 导入了项目中定义的数据传输对象(DTO)类 +import com.sky.dto.OrdersDTO; +import com.sky.dto.OrdersPageQueryDTO; +import com.sky.dto.OrdersPaymentDTO; +// 导入了项目中定义的实体类 +import com.sky.entity.Orders; +// 导入了项目中定义的结果类 +import com.sky.result.PageResult; +import com.sky.result.Result; +// 导入了项目中定义的服务接口 +import com.sky.service.OrderService; +// 导入了项目中定义的视图对象(VO)类 +import com.sky.vo.OrderPaymentVO; +import com.sky.vo.OrderSubmitVO; +import com.sky.vo.OrderVO; +// 导入了Swagger注解,用于API文档的生成 +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +// 导入了Lombok提供的@Slf4j注解,用于简化日志操作 +import lombok.extern.slf4j.Slf4j; +// 导入了Spring框架中的@Autowired注解,用于自动注入Spring管理的Bean +import org.springframework.beans.factory.annotation.Autowired; +// 导入了Spring框架中的@RequestMapping注解,用于定义请求映射 +import org.springframework.web.bind.annotation.*; + +/** + * 用户端订单控制器,提供用户下单、订单支付、订单查询等接口服务。 + */ +// 使用@RestController注解声明这是一个控制器类,并且返回的数据会自动以JSON格式响应 +@RestController +// 使用@RequestMapping注解定义这个控制器类的基础请求映射路径 +@RequestMapping("/user/order") +// 使用@Slf4j注解提供日志功能 +@Slf4j +// 使用@Api注解为这个控制器类添加Swagger文档的描述 +@Api(tags = "C端-订单接口") +public class OrderController { + + // 自动注入OrderService + @Autowired + OrderService orderService; + + /** + * 再来一单接口。 + * + * @param id 订单ID + * @return 操作结果 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("再来一单") + // 使用@PostMapping注解定义POST请求映射 + @PostMapping("/repetition/{id}") + public Result repetition(@PathVariable Long id) { + orderService.repetition(id); + return Result.success(); + } + + /** + * 用户取消订单接口。 + * + * @param id 订单ID + * @return 操作结果 + * @throws Exception 可能抛出的异常 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("取消订单") + // 使用@PutMapping注解定义PUT请求映射 + @PutMapping("/cancel/{id}") + public Result cancel(@PathVariable("id") Long id) throws Exception { + orderService.userCancelById(id); + return Result.success(); + } + + /** + * 用户下单接口。 + * + * @param ordersDTO 订单数据传输对象 + * @return 订单提交视图对象 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("用户下单") + // 使用@RequestMapping注解定义请求映射 + @RequestMapping("/submit") + public Result submitOrder(@RequestBody OrdersDTO ordersDTO) { + log.info("用户下单,订单信息:{}", ordersDTO); + OrderSubmitVO order = orderService.submit(ordersDTO); + + return Result.success(order); + } + + /** + * 订单支付接口。 + * + * @param ordersPaymentDTO 订单支付数据传输对象 + * @return 操作结果 + * @throws Exception 可能抛出的异常 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("订单支付") + // 使用@PutMapping注解定义PUT请求映射 + @PutMapping("/payment") + public Result payment(@RequestBody OrdersPaymentDTO ordersPaymentDTO) throws Exception { + log.info("订单支付:{}", ordersPaymentDTO); + orderService.paySuccess(ordersPaymentDTO.getOrderNumber()); + return Result.success(); + } + + /** + * 催单接口。 + * + * @param id 订单ID + * @return 操作结果 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("催单") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/reminder/{id}") + public Result reminder(@PathVariable Long id) { + log.info("用户催单orderId:{}", id); + orderService.reminder(id); + + return Result.success(); + } + + /** + * 历史订单查询接口。 + * + * @param page 当前页码 + * @param pageSize 每页显示数量 + * @param status 订单状态 + * @return 分页结果 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("历史订单查询") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/historyOrders") + public Result page(int page, int pageSize, Integer status) { + PageResult pageResult = orderService.pageQuery4User(page, pageSize, status); + return Result.success(pageResult); + } + + /** + * 查询订单详情接口。 + * + * @param id 订单ID + * @return 订单视图对象 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("查询订单详情") + // 使用@GetMapping注解定义GET请求映射 + @GetMapping("/orderDetail/{id}") + public Result details(@PathVariable("id") Long id) { + OrderVO orderVO = orderService.details(id); + return Result.success(orderVO); + } +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/controller/user/UserController.java b/sky/sky-server/src/main/java/com/sky/controller/user/UserController.java new file mode 100644 index 0000000..ad39e0b --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/controller/user/UserController.java @@ -0,0 +1,80 @@ +// 定义了一个名为com.sky.controller.user的包,用于存放用户控制器类 +package com.sky.controller.user; + +// 导入了项目中定义的常量类 +import com.sky.constant.JwtClaimsConstant; +// 导入了项目中定义的用户登录数据传输对象(DTO)类 +import com.sky.dto.UserLoginDTO; +// 导入了项目中定义的实体类 +import com.sky.entity.User; +// 导入了项目中定义的JWT属性类 +import com.sky.properties.JwtProperties; +// 导入了项目中定义的结果类 +import com.sky.result.Result; +// 导入了项目中定义的服务接口 +import com.sky.service.UserService; +// 导入了项目中定义的JWT工具类 +import com.sky.utils.JwtUtil; +// 导入了项目中定义的视图对象(VO)类 +import com.sky.vo.UserLoginVO; +// 导入了Swagger注解,用于API文档的生成 +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +// 导入了Lombok提供的@Slf4j注解,用于简化日志操作 +import lombok.extern.slf4j.Slf4j; +// 导入了Spring框架中的@Autowired注解,用于自动注入Spring管理的Bean +import org.springframework.beans.factory.annotation.Autowired; +// 导入了Spring框架中的@RequestMapping注解,用于定义请求映射 +import org.springframework.web.bind.annotation.*; + +/** + * 用户控制器,提供用户登录等接口服务。 + */ +// 使用@RestController注解声明这是一个控制器类,并且返回的数据会自动以JSON格式响应 +@RestController +// 使用@RequestMapping注解定义这个控制器类的基础请求映射路径 +@RequestMapping("/user/user") +// 使用@Slf4j注解提供日志功能 +@Slf4j +// 使用@Api注解为这个控制器类添加Swagger文档的描述 +@Api("C端用户相关接口") +public class UserController { + // 自动注入UserService + @Autowired + UserService userService; + // 自动注入JwtProperties + @Autowired + JwtProperties jwtProperties; + + /** + * 用户登录接口。 + * + * @param userLoginDTO 用户登录数据传输对象 + * @return 用户登录视图对象,包含JWT令牌等信息 + */ + // 使用@ApiOperation注解为这个接口方法添加Swagger文档的描述 + @ApiOperation("用户登录") + // 使用@PostMapping注解定义POST请求映射 + @PostMapping("/login") + public Result login(@RequestBody UserLoginDTO userLoginDTO){ + // 使用日志记录用户登录的操作 + log.info("微信登录:{}",userLoginDTO); + // 调用UserService的wxLogin方法进行微信登录 + User user = userService.wxLogin(userLoginDTO); + + // 生成JWT令牌 + Map claims = new HashMap<>(); + claims.put(JwtClaimsConstant.USER_ID, user.getId()); + String jwt = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims); + + // 构建用户登录视图对象 + UserLoginVO userLoginVO = UserLoginVO.builder() + .id(user.getId()) + .token(jwt) + .openid(user.getOpenid()) + .build(); + + // 返回成功的结果,包含用户登录视图对象 + return Result.success(userLoginVO); + } +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/mapper/AddressBookMapper.java b/sky/sky-server/src/main/java/com/sky/mapper/AddressBookMapper.java new file mode 100644 index 0000000..ef02c66 --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/mapper/AddressBookMapper.java @@ -0,0 +1,71 @@ +// 定义了一个名为com.sky.mapper的包,用于存放MyBatis Mapper接口 +package com.sky.mapper; + +// 导入了项目中定义的AddressBook实体类 +import com.sky.entity.AddressBook; +// 导入了MyBatis相关注解 +import org.apache.ibatis.annotations.*; +// 导入了Java.util.List接口,用于定义返回列表类型 +import java.util.List; + +// 使用@Mapper注解标记这是一个MyBatis Mapper接口 +@Mapper +public interface AddressBookMapper { + + /** + * 条件查询方法,根据传入的AddressBook对象条件查询地址簿信息。 + * + * @param addressBook 条件对象 + * @return 返回符合条件的AddressBook对象列表 + */ + List list(AddressBook addressBook); + + /** + * 新增方法,将一个新的地址簿信息插入到数据库中。 + * + * @param addressBook 要插入的AddressBook对象 + */ + // 使用@Insert注解标记这是一个插入操作,并提供SQL语句 + @Insert("insert into address_book" + + " (user_id, consignee, phone, sex, province_code, province_name, city_code, city_name, district_code," + + " district_name, detail, label, is_default)" + + " values (#{userId}, #{consignee}, #{phone}, #{sex}, #{provinceCode}, #{provinceName}, #{cityCode}, #{cityName}," + + " #{districtCode}, #{districtName}, #{detail}, #{label}, #{isDefault})") + void insert(AddressBook addressBook); + + /** + * 根据id查询方法,根据用户ID查询对应的地址簿信息。 + * + * @param id 用户ID + * @return 返回对应的AddressBook对象 + */ + // 使用@Select注解标记这是一个查询操作,并提供SQL语句 + @Select("select * from address_book where user_id = #{id}") + AddressBook getById(Long id); + + /** + * 根据id修改方法,根据传入的AddressBook对象修改数据库中的地址簿信息。 + * + * @param addressBook 要修改的AddressBook对象 + */ + void update(AddressBook addressBook); + + /** + * 根据用户id修改是否默认地址方法,更新指定用户ID的默认地址状态。 + * + * @param addressBook 包含用户ID和是否默认状态的AddressBook对象 + */ + // 使用@Update注解标记这是一个更新操作,并提供SQL语句 + @Update("update address_book set is_default = #{isDefault} where user_id = #{userId}") + void updateIsDefaultByUserId(AddressBook addressBook); + + /** + * 根据id删除地址方法,根据ID删除数据库中的地址簿信息。 + * + * @param id 要删除的地址簿ID + */ + // 使用@Delete注解标记这是一个删除操作,并提供SQL语句 + @Delete("delete from address_book where id = #{id}") + void deleteById(Long id); + +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/mapper/OrderDetailMapper.java b/sky/sky-server/src/main/java/com/sky/mapper/OrderDetailMapper.java new file mode 100644 index 0000000..5755637 --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/mapper/OrderDetailMapper.java @@ -0,0 +1,35 @@ +// 定义了一个名为com.sky.mapper的包,用于存放MyBatis Mapper接口 +package com.sky.mapper; + +// 导入了项目中定义的OrderDetail实体类 +import com.sky.entity.OrderDetail; +// 导入了MyBatis相关注解 +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +// 导入了Java.util.List接口,用于定义返回列表类型 +import java.util.List; +// 导入了Java.util.ArrayList类,用于定义批量插入的数据类型 +import java.util.ArrayList; + +// 使用@Mapper注解标记这是一个MyBatis Mapper接口 +@Mapper +public interface OrderDetailMapper { + + /** + * 插入多条订单明细数据方法,用于将多条订单明细信息批量插入到数据库中。 + * + * @param orderDetails 要插入的订单明细对象列表 + */ + // 此方法的具体实现需要在MyBatis的映射文件中定义,通常使用标签,并设置typeHandler进行批量插入 + void insertBatch(ArrayList orderDetails); + + /** + * 根据订单id查询订单明细方法,根据订单ID查询对应的订单明细信息。 + * + * @param orderId 订单ID + * @return 返回对应的订单明细对象列表 + */ + // 使用@Select注解标记这是一个查询操作,并提供SQL语句 + @Select("select * from order_detail where order_id = #{orderId}") + List getByOrderId(Long orderId); +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/mapper/ShoppingCartMapper.java b/sky/sky-server/src/main/java/com/sky/mapper/ShoppingCartMapper.java new file mode 100644 index 0000000..726d9ae --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/mapper/ShoppingCartMapper.java @@ -0,0 +1,57 @@ +// 定义了一个名为com.sky.mapper的包,用于存放MyBatis Mapper接口 +package com.sky.mapper; + +// 导入了项目中定义的ShoppingCart实体类 +import com.sky.entity.ShoppingCart; +// 导入了MyBatis相关注解 +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Update; +// 导入了Java.util.List接口,用于定义返回列表类型 +import java.util.List; + +// 使用@Mapper注解标记这是一个MyBatis Mapper接口 +@Mapper +public interface ShoppingCartMapper { + + /** + * 批量插入购物车数据方法,用于将多个购物车记录批量插入到数据库中。 + * + * @param shoppingCartList 要插入的购物车记录列表 + */ + void insertBatch(List shoppingCartList); + + /** + * 查询用户购物车方法,根据传入的ShoppingCart对象条件查询用户的购物车信息。 + * + * @param shoppingCart 条件对象 + * @return 返回符合条件的ShoppingCart对象列表 + */ + List list(ShoppingCart shoppingCart); + + /** + * 根据id修改商品数量方法,用于更新购物车中商品的数量和金额。 + * + * @param shoppingCart 包含更新信息的ShoppingCart对象 + */ + // 使用@Update注解标记这是一个更新操作,并提供SQL语句 + @Update("update shopping_cart set number=#{number},amount=#{amount} where id=#{id}") + void update(ShoppingCart shoppingCart); + + /** + * 插入一个购物车数据方法,用于将单个购物车记录插入到数据库中。 + * + * @param shoppingCart 要插入的ShoppingCart对象 + */ + // 使用@Insert注解标记这是一个插入操作,并提供SQL语句 + @Insert("insert into shopping_cart(name,user_id,dish_id,setmeal_id,dish_flavor,number,amount,image,create_time)" + + "values (#{name},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{number},#{amount},#{image},#{createTime})") + void insert(ShoppingCart shoppingCart); + + /** + * 删除购物车中的一个商品方法,用于根据传入的ShoppingCart对象删除购物车中的一个商品记录。 + * + * @param shoppingCart 包含删除条件的ShoppingCart对象 + */ + void delete(ShoppingCart shoppingCart); +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/service/DishService.java b/sky/sky-server/src/main/java/com/sky/service/DishService.java new file mode 100644 index 0000000..6357892 --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/service/DishService.java @@ -0,0 +1,87 @@ +package com.sky.service; + +import com.sky.dto.DishDTO; +import com.sky.dto.DishPageQueryDTO; +import com.sky.entity.Dish; +import com.sky.result.PageResult; +import com.sky.vo.DishVO; +import com.sky.vo.SetmealVO; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * DishService接口定义了菜品相关的服务方法。 + */ +public interface DishService { + + /** + * 菜品分页查询。 + * 根据DishPageQueryDTO中提供的条件进行分页查询,返回查询结果的分页信息。 + * + * @param dishPageQueryDTO 包含查询条件和分页信息的DTO对象 + * @return 分页结果的PageResult对象 + */ + PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO); + + /** + * 新增菜品和对应的口味数据。 + * 将DishDTO对象中的菜品和口味数据保存到数据库中。 + * + * @param dishDTO 包含菜品和口味信息的DTO对象 + */ + void saveWithFlaver(DishDTO dishDTO); + + /** + * 删除菜品。 + * 根据提供的ID列表批量删除菜品。 + * + * @param ids 要删除的菜品ID列表 + */ + void deleteBatch(ArrayList ids); + + /** + * 修改菜品。 + * 根据DishDTO对象中的信息更新数据库中的菜品数据。 + * + * @param dishDTO 包含菜品信息的DTO对象 + */ + void updateDish(DishDTO dishDTO); + + /** + * 根据id获取菜品数据。 + * 根据提供的ID查询并返回对应的菜品VO对象。 + * + * @param id 菜品的ID + * @return 菜品的DishVO对象 + */ + DishVO getById(Long id); + + /** + * 根据分类id查询菜品。 + * 根据提供的分类ID查询并返回该分类下的所有菜品实体。 + * + * @param categoryId 菜品分类的ID + * @return 菜品实体列表 + */ + ArrayList getByCategoryId(Long categoryId); + + /** + * 起售或停售菜品。 + * 根据提供的ID和状态更新菜品的起售或停售状态。 + * + * @param id 菜品的ID + * @param status 起售或停售状态 + */ + void startOrStop(Long id, Integer status); + + /** + * 条件查询菜品和口味。 + * 根据Dish对象中的条件查询并返回符合条件的菜品VO列表,包含口味信息。 + * + * @param dish 包含查询条件的Dish实体 + * @return 包含口味信息的菜品VO列表 + */ + List listWithFlavor(Dish dish); +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/service/SetmealService.java b/sky/sky-server/src/main/java/com/sky/service/SetmealService.java new file mode 100644 index 0000000..ac9bff0 --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/service/SetmealService.java @@ -0,0 +1,87 @@ +package com.sky.service; + +import com.sky.dto.CategoryPageQueryDTO; +import com.sky.dto.SetmealDTO; +import com.sky.dto.SetmealPageQueryDTO; +import com.sky.entity.Setmeal; +import com.sky.result.PageResult; +import com.sky.vo.DishItemVO; +import com.sky.vo.SetmealVO; + +import java.util.ArrayList; +import java.util.List; + +/** + * SetmealService接口定义了套餐相关的服务方法。 + */ +public interface SetmealService { + + /** + * 条件查询套餐。 + * 根据Setmeal对象中的条件查询并返回符合条件的套餐实体列表。 + * + * @param setmeal 包含查询条件的Setmeal实体 + * @return 套餐实体列表 + */ + List list(Setmeal setmeal); + + /** + * 根据id查询菜品选项。 + * 根据提供的套餐ID查询并返回该套餐包含的菜品项VO列表。 + * + * @param id 套餐的ID + * @return 菜品项VO列表 + */ + List getDishItemById(Long id); + + /** + * 分页查询套餐。 + * 根据SetmealPageQueryDTO中提供的条件进行分页查询,返回查询结果的分页信息。 + * + * @param setmealPageQueryDTO 包含查询条件和分页信息的DTO对象 + * @return 分页结果的PageResult对象 + */ + PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO); + + /** + * 起售或停售套餐。 + * 根据提供的状态和套餐ID更新套餐的起售或停售状态。 + * + * @param status 起售或停售状态 + * @param id 套餐的ID + */ + void startOrStop(Integer status, Long id); + + /** + * 修改套餐信息。 + * 根据SetmealDTO对象中的信息更新数据库中的套餐数据。 + * + * @param setmealDTO 包含套餐信息的DTO对象 + */ + void updateSetmeal(SetmealDTO setmealDTO); + + /** + * 根据套餐id获取套餐。 + * 根据提供的套餐ID查询并返回对应的套餐VO对象。 + * + * @param id 套餐的ID + * @return 套餐的SetmealVO对象 + */ + SetmealVO getDishById(Long id); + + /** + * 根据套餐id批量删除套餐。 + * 根据提供的ID列表批量删除套餐。 + * + * @param ids 要删除的套餐ID列表 + */ + void batchDeleteById(ArrayList ids); + + /** + * 新增套餐。 + * 将SetmealDTO对象中的套餐数据保存到数据库中。 + * + * @param setmealDTO 包含套餐信息的DTO对象 + */ + void insert(SetmealDTO setmealDTO); +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/service/impl/CommonServiceImpl.java b/sky/sky-server/src/main/java/com/sky/service/impl/CommonServiceImpl.java new file mode 100644 index 0000000..f24201c --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/service/impl/CommonServiceImpl.java @@ -0,0 +1,54 @@ +package com.sky.service.impl; + +import com.sky.service.CommonService; +import com.sky.utils.AliOssUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.UUID; + +/** + * CommonServiceImpl类是CommonService接口的实现类,提供了通用服务的实现,如文件上传。 + */ +@Service +@Slf4j +public class CommonServiceImpl implements CommonService { + + /** + * AliOssUtil是一个工具类,用于操作阿里云OSS服务。 + * 通过@Autowired注解,Spring会自动注入AliOssUtil的实例。 + */ + @Autowired + private AliOssUtil aliOssUtil; + + /** + * upload方法用于上传文件到OSS。 + * 它接受一个MultipartFile类型的参数,这是Spring MVC处理上传文件的类型。 + * 方法返回上传后的文件路径。 + * + * @param file 要上传的文件 + * @return 返回上传文件后在OSS上的路径 + */ + @Override + public String upload(MultipartFile file) { + String filePath = null; + // 获取原始文件名 + String originalFilename = file.getOriginalFilename(); + // 从原始文件名中截取文件后缀 + String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); + // 构造新的文件名,使用UUID确保唯一性,加上原文件后缀 + String objectName = UUID.randomUUID() + extension; + try { + // 使用aliOssUtil上传文件,并获取文件路径 + filePath = aliOssUtil.upload(file.getBytes(), objectName); + } catch (IOException e) { + // 如果上传过程中发生IO异常,记录错误日志 + log.error("文件上传失败:{}", e); + } + // 返回文件路径 + return filePath; + } +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/service/impl/ReportServiceImpl.java b/sky/sky-server/src/main/java/com/sky/service/impl/ReportServiceImpl.java new file mode 100644 index 0000000..12a80df --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/service/impl/ReportServiceImpl.java @@ -0,0 +1,364 @@ +package com.sky.service.impl; + +import com.sky.entity.Orders; +import com.sky.mapper.ReportMapper; +import com.sky.properties.ReportExcelProperties; +import com.sky.result.Result; +import com.sky.service.ReportService; +import com.sky.vo.*; +import io.swagger.models.auth.In; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.extractor.ExcelExtractor; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.xssf.usermodel.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.stereotype.Service; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.math.BigDecimal; +import java.nio.file.Files; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; +import java.util.*; + +@Service +@Slf4j +public class ReportServiceImpl implements ReportService { + + @Autowired + ReportMapper reportMapper; + @Autowired + ReportExcelProperties reportExcelProperties; + @Autowired + WorkspaceServiceImpl workspaceService; + + /** + * 营业额统计 + * + * @param begin + * @param end + * @return + */ + @Override + public Result getTurnoverStatistics(LocalDate begin, LocalDate end) { + LocalDate p = begin; + ArrayList dateTimes = new ArrayList<>(); + while (!p.equals(end)) { + dateTimes.add(p); + p = p.plusDays(1); + } + ArrayList count = new ArrayList<>(); + for (LocalDate dateTime : dateTimes) { + HashMap map = new HashMap<>(); + map.put("status", 5); + map.put("begin", LocalDateTime.of(dateTime, LocalTime.MIN)); + map.put("end", LocalDateTime.of(dateTime, LocalTime.MAX)); + Double sum = reportMapper.sumByMap(map); + if (sum == null) { + sum = 0D; + } + count.add(sum); + } + + return Result.success(TurnoverReportVO.builder() + .dateList(StringUtils.join(dateTimes, ",")) + .turnoverList(StringUtils.join(count, ",")) + .build()); + } + + /** + * 用户统计 + * + * @param begin + * @param end + * @return + */ + @Override + public UserReportVO userStatistics(LocalDate begin, LocalDate end) { + LocalDate p = begin; + ArrayList dateTimes = new ArrayList<>(); + while (!p.equals(end)) { + dateTimes.add(p); + p = p.plusDays(1); + } + ArrayList user = new ArrayList<>(); + ArrayList newUser = new ArrayList<>(); + for (LocalDate dateTime : dateTimes) { + HashMap map = new HashMap<>(); + map.put("begin", LocalDateTime.of(dateTime, LocalTime.MIN)); + map.put("end", LocalDateTime.of(dateTime, LocalTime.MAX)); + Integer sum = reportMapper.sumUserByDay(map); + Integer sumUser = reportMapper.sumUser(map); + if (sum == null) { + sum = 0; + } + if (sumUser == null) { + sumUser = 0; + } + newUser.add(sum); + user.add(sumUser); + } + + + return UserReportVO.builder() + .dateList(StringUtils.join(dateTimes, ",")) + .totalUserList(StringUtils.join(user, ",")) + .newUserList(StringUtils.join(newUser, ",")) + .build(); + } + + /** + * 订单统计 + * + * @param begin + * @param end + * @return + */ + @Override + public OrderReportVO orderStatistics(LocalDate begin, LocalDate end) { + // 初始化日期列表,用于存储开始和结束日期之间的每一天 + LocalDate p = begin; + ArrayList dateTimes = new ArrayList<>(); + while (!p.equals(end)) { + dateTimes.add(p); + p = p.plusDays(1); + } + + // 初始化订单数量和新订单数量的列表 + ArrayList order = new ArrayList<>(); + ArrayList newOrder = new ArrayList<>(); + + // 遍历每一天,统计订单数量和新订单数量 + for (LocalDate dateTime : dateTimes) { + HashMap map = new HashMap<>(); + map.put("begin", LocalDateTime.of(dateTime, LocalTime.MIN)); // 当天的最小时间 + map.put("end", LocalDateTime.of(dateTime, LocalTime.MAX)); // 当天的最大时间 + map.put("status", Orders.COMPLETED); // 完成的订单状态 + + // 调用reportMapper查询新订单和订单总数 + Integer sum = reportMapper.sumNewOrder(map); + Integer sumOrder = reportMapper.sumOrder(map); + + // 处理查询结果为null的情况 + if (sum == null) { + sum = 0; + } + if (sumOrder == null) { + sumOrder = 0; + } + + // 将查询结果添加到列表中 + order.add(sumOrder); + newOrder.add(sum); + } + + // 计算订单完成率 + Double rate; + if (sumArrayList(order) == 0) { + rate = 1.0; // 如果订单总数为0,则完成率为1.0 + } else { + rate = sumArrayList(newOrder) / sumArrayList(order) * 1.0; // 计算完成率 + } + + // 构建并返回订单统计报告对象 + return OrderReportVO.builder() + .dateList(StringUtils.join(dateTimes, ",")) + // 日期列表,以逗号分隔 + .orderCountList(StringUtils.join(newOrder, ",")) + // 新订单数量列表,以逗号分隔 + .validOrderCountList(StringUtils.join(order, ",")) + // 订单数量列表,以逗号分隔 + .totalOrderCount(sumArrayList(newOrder)) + // 新订单总数 + .validOrderCount(sumArrayList(order)) + // 订单总数 + .orderCompletionRate(rate) + // 订单完成率 + .build(); + // 构建报告对象 + } + + /** + * 销量排行前十 + * + * @param begin + * @param end + * @return + */ + @Override + public SalesTop10ReportVO salesTop10Report(LocalDate begin, LocalDate end) { + // 初始化商品名称和销售数量的列表 + ArrayList name = new ArrayList<>(); + ArrayList number = new ArrayList<>(); + + // 创建映射,用于传递查询参数 + HashMap map = new HashMap<>(); + // 查询结果集 + ArrayList> result; + + // 设置查询参数 + map.put("begin", LocalDateTime.of(begin, LocalTime.MIN)); // 查询开始时间(当天的最小时间) + map.put("end", LocalDateTime.of(end, LocalTime.MAX)); // 查询结束时间(当天的最大时间) + map.put("status", Orders.COMPLETED); // 订单状态,这里为已完成的订单 + + // 调用reportMapper查询销售排行前十的商品 + result = reportMapper.salesTop10Report(map); + + // 遍历查询结果,提取商品名称和销售数量 + for (HashMap hashMap : result) { + name.add((String) hashMap.get("name")); // 获取商品名称 + number.add(((BigDecimal) hashMap.get("number")).intValue()); // 获取销售数量并转换为整数 + } + + // 构建并返回销售排行报告对象 + return SalesTop10ReportVO.builder() + .nameList(StringUtils.join(name, ",")) // 将商品名称列表转换为逗号分隔的字符串 + .numberList(StringUtils.join(number, ",")) // 将销售数量列表转换为逗号分隔的字符串 + .build(); // 构建报告对象 + } + + /** + * 导出excel表格 + */ + @Override + public void export(HttpServletResponse httpResponse) throws IOException, InvalidFormatException { + File file = new File(reportExcelProperties.getFilePath()); + OPCPackage opcPackage = OPCPackage.open(file); + //获取工作薄 + XSSFWorkbook workbook = new XSSFWorkbook(opcPackage); + String s = reportExcelProperties.getSheet()[0]; + //获取工作表 + XSSFSheet sheet = workbook.getSheet(s); + //填写日期 + XSSFRow row = sheet.getRow(1); + XSSFCellStyle dataStyle = workbook.createCellStyle(); + //设置日期的字体 + XSSFFont font = workbook.createFont(); + font.setFontHeight(16); + font.setFontName("宋体"); + dataStyle.setAlignment(HorizontalAlignment.RIGHT); + XSSFCell cell0 = row.getCell(1); + dataStyle.setFont(font); + cell0.setCellStyle(dataStyle); + + + //获取营业概览数据 + LocalDateTime begin = LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN); + LocalDateTime end = LocalDateTime.now().with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); + cell0.setCellValue(begin.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")) + "——" + end.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"))); + BusinessDataVO businessData = workspaceService.getBusinessData(begin, end); + XSSFRow row3 = sheet.getRow(3); + //营业额 + XSSFCell cell = row3.getCell(2); + cell.setCellValue(businessData.getTurnover()); + //订单完成率 + XSSFCell cell1 = row3.getCell(4); + cell1.setCellValue(businessData.getOrderCompletionRate()); + //新增用户数 + XSSFCell cell2 = row3.getCell(6); + cell2.setCellValue(businessData.getNewUsers()); + //有效订单 + XSSFRow row1 = sheet.getRow(4); + XSSFCell cell3 = row1.getCell(2); + cell3.setCellValue(businessData.getValidOrderCount()); + //平均客单价 + XSSFCell cell4 = row1.getCell(4); + cell4.setCellValue(businessData.getValidOrderCount()); + int dayOfMonth = Period.between(begin.toLocalDate(), end.toLocalDate()).getDays(); + System.out.println("dayOfMonth:" + dayOfMonth); + //获取明细数据 + + for (int i = 0; i < dayOfMonth; i++) { + XSSFRow row2 = sheet.getRow(i + 7); + LocalDateTime localDateTimeBegin = begin.plusDays(i); + LocalDateTime localDateTimeEnd = localDateTimeBegin.with(LocalTime.MAX); + BusinessDataVO data = workspaceService.getBusinessData(localDateTimeBegin, localDateTimeEnd); + //设置日期 + row2.getCell(1).setCellValue(localDateTimeBegin.toLocalDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"))); + //营业额 + row2.getCell(2).setCellValue(data.getTurnover()); + //有效订单 + row2.getCell(3).setCellValue(data.getValidOrderCount()); + //订单完成率 + row2.getCell(4).setCellValue(data.getOrderCompletionRate()); + //平均客单价 + row2.getCell(5).setCellValue(data.getUnitPrice()); + //新增用户数 + row2.getCell(6).setCellValue(data.getNewUsers()); + } + +/** + * 获取Servlet输出流,用于写入响应数据。 + * 这个输出流允许我们将二进制数据直接写入HTTP响应中。 + */ + ServletOutputStream outputStream = httpResponse.getOutputStream(); + +/** + * 重置HTTP响应状态和头部信息。 + * 调用reset()方法会清除任何存在的响应信息,包括状态码和头部信息, + * 这样我们可以设置新的响应内容和头部信息。 + */ + httpResponse.reset(); + +/** + * 设置响应内容类型为Excel文件。 + * 通过设置ContentType为"application/vnd.ms-excel",我们告诉客户端响应的内容类型是Excel文件。 + */ + httpResponse.setContentType("application/vnd.ms-excel"); + +/** + * 添加响应头部,指示浏览器这是一个附件,并为其指定文件名。 + * Content-disposition头部用于指定响应的用途,这里我们设置为"attachment"表示附件, + * 并指定下载时的默认文件名为"template.xlsx"。 + */ + httpResponse.addHeader("Content-disposition", "attachment;filename=template.xlsx"); + +/** + * 将Excel工作簿写入Servlet输出流。 + * 使用Apache POI库的write方法,我们将Excel工作簿(workbook)的内容写入到前面获取的输出流中。 + */ + workbook.write(outputStream); + +/** + * 刷新输出流,确保所有数据都被写入。 + * 调用flush()方法可以确保输出流中的所有数据都被推送到客户端。 + */ + outputStream.flush(); + +/** + * 关闭输出流,释放资源。 + * 完成数据写入后,我们需要关闭输出流以释放系统资源。 + */ + outputStream.close(); + + } + + /** + * 计算整数列表的总和。 + * 这个方法接受一个ArrayList类型的参数,遍历列表中的每个元素,并将它们相加。 + * + * @param arrayList 要计算总和的整数列表 + * @return 返回列表中所有整数的总和 + */ + private Integer sumArrayList(ArrayList arrayList) { + Integer sum = 0; // 初始化总和为0 + for (Integer integer : arrayList) { // 遍历列表中的每个整数 + sum += integer; // 将当前整数加到总和上 + } + return sum; // 返回计算出的总和 + } +} diff --git a/sky/sky-server/src/main/java/com/sky/service/impl/WorkspaceServiceImpl.java b/sky/sky-server/src/main/java/com/sky/service/impl/WorkspaceServiceImpl.java new file mode 100644 index 0000000..09e096b --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/service/impl/WorkspaceServiceImpl.java @@ -0,0 +1,168 @@ +package com.sky.service.impl; + +import com.sky.constant.StatusConstant; +import com.sky.entity.Orders; +import com.sky.mapper.DishMapper; +import com.sky.mapper.OrderMapper; +import com.sky.mapper.SetmealMapper; +import com.sky.mapper.UserMapper; +import com.sky.service.WorkspaceService; +import com.sky.vo.BusinessDataVO; +import com.sky.vo.DishOverViewVO; +import com.sky.vo.OrderOverViewVO; +import com.sky.vo.SetmealOverViewVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * WorkspaceServiceImpl类是WorkspaceService接口的实现类,提供工作空间相关的服务。 + */ +@Service +@Slf4j +public class WorkspaceServiceImpl implements WorkspaceService { + + // 自动注入OrderMapper,用于操作订单相关的数据库操作 + @Autowired + private OrderMapper orderMapper; + // 自动注入UserMapper,用于操作用户相关的数据库操作 + @Autowired + private UserMapper userMapper; + // 自动注入DishMapper,用于操作菜品相关的数据库操作 + @Autowired + private DishMapper dishMapper; + // 自动注入SetmealMapper,用于操作套餐相关的数据库操作 + @Autowired + private SetmealMapper setmealMapper; + + /** + * 根据时间段统计营业数据。 + * 包括营业额、有效订单数、订单完成率、平均客单价和新增用户数。 + * + * @param begin 查询开始时间 + * @param end 查询结束时间 + * @return 返回营业数据的BusinessDataVO对象 + */ + public BusinessDataVO getBusinessData(LocalDateTime begin, LocalDateTime end) { + // 初始化查询参数的HashMap + HashMap map = new HashMap<>(); + map.put("begin", begin); + map.put("end", end); + map.put("status", null); + + // 查询总订单数 + Integer totalOrderCount = orderMapper.countByMap(map); + + // 设置状态为已完成,查询营业额和有效订单数 + map.put("status", Orders.COMPLETED); + Double turnover = orderMapper.sumByMap(map); + turnover = turnover == null ? 0.0 : turnover; + + Integer validOrderCount = orderMapper.countByMap(map); + + // 计算订单完成率和平均客单价 + Double unitPrice = 0.0; + Double orderCompletionRate = 0.0; + if (totalOrderCount != 0 && validOrderCount != 0) { + orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount; + unitPrice = turnover / validOrderCount; + } + + // 查询新增用户数 + Integer newUsers = userMapper.countByMap(map); + + // 构建并返回营业数据对象 + return BusinessDataVO.builder() + .turnover(turnover) + .validOrderCount(validOrderCount) + .orderCompletionRate(orderCompletionRate) + .unitPrice(unitPrice) + .newUsers(newUsers) + .build(); + } + + /** + * 查询订单管理数据,包括待接单、待派送、已完成、已取消和全部订单的数量。 + * + * @return 返回订单管理数据的OrderOverViewVO对象 + */ + public OrderOverViewVO getOrderOverView() { + Map map = new HashMap(); + map.put("begin", LocalDateTime.now().with(LocalTime.MIN)); + map.put("end", LocalDateTime.now().with(LocalTime.MAX)); + map.put("status", Orders.TO_BE_CONFIRMED); + + // 查询待接单数量 + Integer waitingOrders = orderMapper.countByMap(map); + + // 查询待派送数量 + map.put("status", Orders.CONFIRMED); + Integer deliveredOrders = orderMapper.countByMap(map); + + // 查询已完成数量 + map.put("status", Orders.COMPLETED); + Integer completedOrders = orderMapper.countByMap(map); + + // 查询已取消数量 + map.put("status", Orders.CANCELLED); + Integer cancelledOrders = orderMapper.countByMap(map); + + // 查询全部订单数量 + map.put("status", null); + Integer allOrders = orderMapper.countByMap(map); + + // 构建并返回订单管理数据对象 + return OrderOverViewVO.builder() + .waitingOrders(waitingOrders) + .deliveredOrders(deliveredOrders) + .completedOrders(completedOrders) + .cancelledOrders(cancelledOrders) + .allOrders(allOrders) + .build(); + } + + /** + * 查询菜品总览,包括在售和已下架的菜品数量。 + * + * @return 返回菜品总览数据的DishOverViewVO对象 + */ + public DishOverViewVO getDishOverView() { + Map map = new HashMap(); + map.put("status", StatusConstant.ENABLE); + Integer sold = dishMapper.countByMap(map); + + map.put("status", StatusConstant.DISABLE); + Integer discontinued = dishMapper.countByMap(map); + + // 构建并返回菜品总览数据对象 + return DishOverViewVO.builder() + .sold(sold) + .discontinued(discontinued) + .build(); + } + + /** + * 查询套餐总览,包括在售和已下架的套餐数量。 + * + * @return 返回套餐总览数据的SetmealOverViewVO对象 + */ + public SetmealOverViewVO getSetmealOverView() { + Map map = new HashMap(); + map.put("status", StatusConstant.ENABLE); + Integer sold = setmealMapper.countByMap(map); + + map.put("status", StatusConstant.DISABLE); + Integer discontinued = setmealMapper.countByMap(map); + + // 构建并返回套餐总览数据对象 + return SetmealOverViewVO.builder() + .sold(sold) + .discontinued(discontinued) + .build(); + } +} \ No newline at end of file diff --git a/sky/sky-server/src/main/java/com/sky/task/OrderTask.java b/sky/sky-server/src/main/java/com/sky/task/OrderTask.java new file mode 100644 index 0000000..27329a3 --- /dev/null +++ b/sky/sky-server/src/main/java/com/sky/task/OrderTask.java @@ -0,0 +1,57 @@ +package com.sky.task; + +import com.sky.entity.Orders; +import com.sky.mapper.OrderMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * OrderTask类定义了与订单相关的定时任务,用于处理订单超时和一直处于派送中的订单。 + */ +@Component +@Slf4j +public class OrderTask { + @Autowired + private OrderMapper orderMapper; // 注入OrderMapper,用于数据库操作 + + /** + * 处理订单超时的方法,每分钟触发一次。 + * 此方法会查找所有待支付且下单时间超过15分钟的订单,并将它们的状态设置为已取消。 + */ + @Scheduled(cron = "0 * * * * ?") + public void processTimeOrderTask() { + log.info("处理订单超时:{}", LocalDateTime.now()); // 记录当前时间 + LocalDateTime time = LocalDateTime.now().plusMinutes(-15); // 设置15分钟前的时间 + ArrayList ordersList = (ArrayList) orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time); // 查询待支付且下单时间超过15分钟的订单 + if (ordersList != null && ordersList.size() > 0) { // 如果存在这样的订单 + for (int i = 0; i < ordersList.size(); i++) { // 遍历这些订单 + ordersList.get(i).setStatus(Orders.CANCELLED); // 设置状态为已取消 + ordersList.get(i).setCancelReason("订单超时"); // 设置取消原因为订单超时 + } + orderMapper.updateBatchStatus(ordersList, Orders.CANCELLED); // 更新订单状态 + } + } + + /** + * 处理一直处于派送中状态的订单,每天凌晨1点触发一次。 + * 此方法会查找所有一直处于派送中的订单,并将它们的状态设置为已完成。 + */ + @Scheduled(cron = "0 0 1 * * ?") + public void processDeliveryOrder() { + log.info("定时处理处于派送中的订单:{}", LocalDateTime.now()); // 记录当前时间 + + ArrayList ordersList = (ArrayList) orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, null); // 查询一直处于派送中的订单 + if (ordersList != null && ordersList.size() > 0) { // 如果存在这样的订单 + for (int i = 0; i < ordersList.size(); i++) { // 遍历这些订单 + ordersList.get(i).setStatus(Orders.COMPLETED); // 设置状态为已完成 + } + orderMapper.updateBatchStatus(ordersList, Orders.COMPLETED); // 更新订单状态 + } + } +} \ No newline at end of file