zhouhui.jiang
Showing 30 changed files with 742 additions and 38 deletions
This diff is collapsed. Click to expand it.
1 package com.apple.erp.config; 1 package com.apple.erp.config;
2 -
3 -import com.apple.erp.utils.JwtUtils;
4 import org.springframework.beans.factory.annotation.Autowired; 2 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.context.annotation.Bean; 3 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration; 4 import org.springframework.context.annotation.Configuration;
...@@ -34,9 +32,6 @@ import java.util.Arrays; ...@@ -34,9 +32,6 @@ import java.util.Arrays;
34 public class SecurityConfig { 32 public class SecurityConfig {
35 33
36 @Autowired 34 @Autowired
37 - private JwtUtils jwtUtils;
38 -
39 - @Autowired
40 private JwtAuthenticationFilter jwtAuthenticationFilter; 35 private JwtAuthenticationFilter jwtAuthenticationFilter;
41 36
42 @Autowired 37 @Autowired
...@@ -72,6 +67,7 @@ public class SecurityConfig { ...@@ -72,6 +67,7 @@ public class SecurityConfig {
72 .antMatchers("/api/auth/**").permitAll() 67 .antMatchers("/api/auth/**").permitAll()
73 .antMatchers("/api/public/**").permitAll() 68 .antMatchers("/api/public/**").permitAll()
74 .antMatchers("/api/test/public").permitAll() // 允许测试接口 69 .antMatchers("/api/test/public").permitAll() // 允许测试接口
70 + .antMatchers("/api/dashboard/**").authenticated() // 首页统计需要认证
75 .antMatchers("/actuator/**").permitAll() 71 .antMatchers("/actuator/**").permitAll()
76 .antMatchers("/druid/**").permitAll() 72 .antMatchers("/druid/**").permitAll()
77 .antMatchers("/swagger-ui/**").permitAll() 73 .antMatchers("/swagger-ui/**").permitAll()
......
...@@ -9,6 +9,7 @@ import com.apple.erp.dto.response.UserInfoRes; ...@@ -9,6 +9,7 @@ import com.apple.erp.dto.response.UserInfoRes;
9 import com.apple.erp.entity.SysUser; 9 import com.apple.erp.entity.SysUser;
10 import com.apple.erp.service.SysUserService; 10 import com.apple.erp.service.SysUserService;
11 import com.apple.erp.service.SysLogService; 11 import com.apple.erp.service.SysLogService;
12 +import com.apple.erp.service.SessionService;
12 import com.apple.erp.utils.JwtUtils; 13 import com.apple.erp.utils.JwtUtils;
13 import io.swagger.v3.oas.annotations.Operation; 14 import io.swagger.v3.oas.annotations.Operation;
14 import io.swagger.v3.oas.annotations.Parameter; 15 import io.swagger.v3.oas.annotations.Parameter;
...@@ -24,7 +25,9 @@ import org.springframework.web.bind.annotation.*; ...@@ -24,7 +25,9 @@ import org.springframework.web.bind.annotation.*;
24 25
25 import javax.servlet.http.HttpServletRequest; 26 import javax.servlet.http.HttpServletRequest;
26 import javax.validation.Valid; 27 import javax.validation.Valid;
28 +import java.util.HashMap;
27 import java.util.List; 29 import java.util.List;
30 +import java.util.Map;
28 31
29 /** 32 /**
30 * 认证控制器 33 * 认证控制器
...@@ -52,6 +55,9 @@ public class AuthController { ...@@ -52,6 +55,9 @@ public class AuthController {
52 @Autowired 55 @Autowired
53 private SysLogService sysLogService; 56 private SysLogService sysLogService;
54 57
58 + @Autowired
59 + private SessionService sessionService;
60 +
55 /** 61 /**
56 * 用户登录 62 * 用户登录
57 */ 63 */
...@@ -96,6 +102,21 @@ public class AuthController { ...@@ -96,6 +102,21 @@ public class AuthController {
96 String token = jwtUtils.generateToken(username); 102 String token = jwtUtils.generateToken(username);
97 String refreshToken = jwtUtils.generateRefreshToken(username); 103 String refreshToken = jwtUtils.generateRefreshToken(username);
98 104
105 + // 将用户会话信息存储到Redis
106 + try {
107 + Map<String, Object> sessionData = new HashMap<>();
108 + sessionData.put("username", username);
109 + sessionData.put("loginTime", System.currentTimeMillis());
110 + sessionData.put("clientIp", getClientIp(request));
111 + sessionData.put("token", token);
112 +
113 + sessionService.storeUserSession(username, sessionData);
114 + log.info("用户会话已存储: {}", username);
115 + } catch (Exception e) {
116 + log.error("存储用户会话失败", e);
117 + // Redis失败不影响登录流程
118 + }
119 +
99 LoginRes loginResponse = new LoginRes(token, refreshToken, username); 120 LoginRes loginResponse = new LoginRes(token, refreshToken, username);
100 ApiRes<LoginRes> response = ApiRes.success("登录成功", loginResponse); 121 ApiRes<LoginRes> response = ApiRes.success("登录成功", loginResponse);
101 return ResponseEntity.ok(response); 122 return ResponseEntity.ok(response);
...@@ -192,6 +213,16 @@ public class AuthController { ...@@ -192,6 +213,16 @@ public class AuthController {
192 } 213 }
193 } 214 }
194 215
216 + // 清除Redis中的用户会话
217 + if (username != null) {
218 + try {
219 + sessionService.removeUserSession(username);
220 + log.info("用户会话已清除: {}", username);
221 + } catch (Exception e) {
222 + log.error("清除用户会话失败", e);
223 + }
224 + }
225 +
195 // 清除Spring Security上下文 226 // 清除Spring Security上下文
196 SecurityContextHolder.clearContext(); 227 SecurityContextHolder.clearContext();
197 228
......
1 +package com.apple.erp.controller;
2 +
3 +import com.apple.erp.dto.response.ApiRes;
4 +import com.apple.erp.dto.response.DashboardStatsRes;
5 +import com.apple.erp.service.DashboardService;
6 +import lombok.extern.slf4j.Slf4j;
7 +import org.springframework.beans.factory.annotation.Autowired;
8 +import org.springframework.web.bind.annotation.GetMapping;
9 +import org.springframework.web.bind.annotation.RequestMapping;
10 +import org.springframework.web.bind.annotation.RestController;
11 +
12 +import java.util.List;
13 +import java.util.Map;
14 +
15 +/**
16 + * 首页仪表板控制器
17 + * 提供首页统计数据接口
18 + */
19 +@Slf4j
20 +@RestController
21 +@RequestMapping("/api/dashboard")
22 +public class DashboardController {
23 +
24 + @Autowired
25 + private DashboardService dashboardService;
26 +
27 + /**
28 + * 获取首页统计数据
29 + * @return 统计数据
30 + */
31 + @GetMapping("/stats")
32 + public ApiRes<DashboardStatsRes> getDashboardStats() {
33 + log.info("获取首页统计数据请求");
34 + try {
35 + DashboardStatsRes result = dashboardService.getDashboardStats();
36 + log.info("首页统计数据获取成功: {}", result);
37 + return ApiRes.success("获取统计数据成功", result);
38 + } catch (Exception e) {
39 + log.error("获取首页统计数据失败", e);
40 + return ApiRes.error(500, "获取统计数据失败: " + e.getMessage());
41 + }
42 + }
43 +
44 + /**
45 + * 测试接口
46 + * @return 测试数据
47 + */
48 + @GetMapping("/test")
49 + public ApiRes<DashboardStatsRes> getTestStats() {
50 + log.info("测试接口调用");
51 + DashboardStatsRes stats = new DashboardStatsRes();
52 + stats.setTodayOrderCount(10);
53 + stats.setPendingDeliveryCount(5);
54 + stats.setPendingInvoiceCount(3);
55 + stats.setTodaySalesAmount(10000.0);
56 + stats.setOnlineUserCount(100);
57 + stats.setSystemStatus("测试正常");
58 + stats.setSystemVersion("v1.0.0");
59 +
60 + return ApiRes.success("测试成功", stats);
61 + }
62 +
63 + /**
64 + * 获取最近活动
65 + * @return 最近活动列表
66 + */
67 + @GetMapping("/activities")
68 + public ApiRes<List<Map<String, Object>>> getRecentActivities() {
69 + log.info("获取最近活动请求");
70 + try {
71 + List<Map<String, Object>> activities = dashboardService.getRecentActivities();
72 + return ApiRes.success("获取最近活动成功", activities);
73 + } catch (Exception e) {
74 + log.error("获取最近活动失败", e);
75 + return ApiRes.error(500, "获取最近活动失败: " + e.getMessage());
76 + }
77 + }
78 +}
...@@ -6,12 +6,14 @@ import com.apple.erp.dto.ExceptionWorkorderRes; ...@@ -6,12 +6,14 @@ import com.apple.erp.dto.ExceptionWorkorderRes;
6 import com.apple.erp.dto.ExceptionWorkorderStatusUpdateReq; 6 import com.apple.erp.dto.ExceptionWorkorderStatusUpdateReq;
7 import com.apple.erp.dto.ExceptionWorkorderUpdateReq; 7 import com.apple.erp.dto.ExceptionWorkorderUpdateReq;
8 import com.apple.erp.service.ExceptionWorkorderService; 8 import com.apple.erp.service.ExceptionWorkorderService;
9 +import com.apple.erp.service.ExcelExportService;
9 import com.apple.erp.dto.response.ApiRes; 10 import com.apple.erp.dto.response.ApiRes;
10 import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 11 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
11 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
12 import io.swagger.v3.oas.annotations.Parameter; 13 import io.swagger.v3.oas.annotations.Parameter;
13 import io.swagger.v3.oas.annotations.tags.Tag; 14 import io.swagger.v3.oas.annotations.tags.Tag;
14 import org.springframework.beans.factory.annotation.Autowired; 15 import org.springframework.beans.factory.annotation.Autowired;
16 +import org.springframework.http.ResponseEntity;
15 import org.springframework.security.access.prepost.PreAuthorize; 17 import org.springframework.security.access.prepost.PreAuthorize;
16 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
17 import org.springframework.web.bind.annotation.*; 19 import org.springframework.web.bind.annotation.*;
...@@ -34,6 +36,9 @@ public class ExceptionWorkorderController { ...@@ -34,6 +36,9 @@ public class ExceptionWorkorderController {
34 @Autowired 36 @Autowired
35 private ExceptionWorkorderService exceptionWorkorderService; 37 private ExceptionWorkorderService exceptionWorkorderService;
36 38
39 + @Autowired
40 + private ExcelExportService excelExportService;
41 +
37 @Operation(summary = "分页查询异常工单列表", description = "根据条件分页查询异常工单列表") 42 @Operation(summary = "分页查询异常工单列表", description = "根据条件分页查询异常工单列表")
38 @GetMapping("/list") 43 @GetMapping("/list")
39 @PreAuthorize("hasAuthority('exception:workorder:list')") 44 @PreAuthorize("hasAuthority('exception:workorder:list')")
...@@ -168,4 +173,22 @@ public class ExceptionWorkorderController { ...@@ -168,4 +173,22 @@ public class ExceptionWorkorderController {
168 List<ExceptionWorkorderRes> stats = exceptionWorkorderService.getExceptionWorkorderStats(); 173 List<ExceptionWorkorderRes> stats = exceptionWorkorderService.getExceptionWorkorderStats();
169 return ApiRes.success(stats); 174 return ApiRes.success(stats);
170 } 175 }
176 +
177 + @Operation(summary = "导出异常工单数据", description = "根据查询条件导出异常工单数据到Excel")
178 + @PostMapping("/export")
179 + @PreAuthorize("hasAuthority('exception:workorder:export')")
180 + public ResponseEntity<byte[]> exportExceptionWorkorders(@RequestBody(required = false) ExceptionWorkorderQueryReq queryReq) {
181 + try {
182 + // 如果请求体为空,创建默认查询条件
183 + if (queryReq == null) {
184 + queryReq = new ExceptionWorkorderQueryReq();
185 + }
186 + // 获取异常工单数据
187 + List<ExceptionWorkorderRes> workorders = exceptionWorkorderService.getExceptionWorkorderListForExport(queryReq);
188 + // 导出Excel
189 + return excelExportService.exportExceptionWorkorders(workorders);
190 + } catch (Exception e) {
191 + throw new RuntimeException("导出异常工单数据失败: " + e.getMessage());
192 + }
193 + }
171 } 194 }
......
1 +package com.apple.erp.dto.response;
2 +
3 +import lombok.Data;
4 +
5 +/**
6 + * 首页统计数据响应DTO
7 + */
8 +@Data
9 +public class DashboardStatsRes {
10 +
11 + /**
12 + * 今日订单数量
13 + */
14 + private Integer todayOrderCount;
15 +
16 + /**
17 + * 待出库数量
18 + */
19 + private Integer pendingDeliveryCount;
20 +
21 + /**
22 + * 待开票数量
23 + */
24 + private Integer pendingInvoiceCount;
25 +
26 + /**
27 + * 今日销售额
28 + */
29 + private Double todaySalesAmount;
30 +
31 + /**
32 + * 在线用户数
33 + */
34 + private Integer onlineUserCount;
35 +
36 + /**
37 + * 系统状态
38 + */
39 + private String systemStatus;
40 +
41 + /**
42 + * 系统版本
43 + */
44 + private String systemVersion;
45 +
46 + /**
47 + * 今日订单增长率
48 + */
49 + private String orderGrowthRate;
50 +
51 + /**
52 + * 今日销售额增长率
53 + */
54 + private String salesGrowthRate;
55 +}
...@@ -54,4 +54,12 @@ public interface ExceptionWorkorderMapper extends BaseMapper<ExceptionWorkorder> ...@@ -54,4 +54,12 @@ public interface ExceptionWorkorderMapper extends BaseMapper<ExceptionWorkorder>
54 int batchUpdateWorkorderStatus(@Param("workorderIds") List<Long> workorderIds, 54 int batchUpdateWorkorderStatus(@Param("workorderIds") List<Long> workorderIds,
55 @Param("workorderStatus") Integer workorderStatus, 55 @Param("workorderStatus") Integer workorderStatus,
56 @Param("handlerUser") String handlerUser); 56 @Param("handlerUser") String handlerUser);
57 +
58 + /**
59 + * 获取异常工单列表用于导出
60 + *
61 + * @param queryReq 查询条件
62 + * @return 异常工单列表(不分页)
63 + */
64 + List<ExceptionWorkorderRes> selectExceptionWorkorderListForExport(@Param("query") ExceptionWorkorderQueryReq queryReq);
57 } 65 }
......
1 +package com.apple.erp.service;
2 +
3 +import com.apple.erp.dto.response.DashboardStatsRes;
4 +
5 +import java.util.List;
6 +import java.util.Map;
7 +
8 +/**
9 + * 首页仪表板服务接口
10 + */
11 +public interface DashboardService {
12 +
13 + /**
14 + * 获取首页统计数据
15 + * @return 统计数据
16 + */
17 + DashboardStatsRes getDashboardStats();
18 +
19 + /**
20 + * 获取最近活动
21 + * @return 最近活动列表
22 + */
23 + List<Map<String, Object>> getRecentActivities();
24 +}
...@@ -67,6 +67,20 @@ public interface DeliveryMainService extends IService<DeliveryMain> { ...@@ -67,6 +67,20 @@ public interface DeliveryMainService extends IService<DeliveryMain> {
67 * @return 是否成功 67 * @return 是否成功
68 */ 68 */
69 boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus); 69 boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus);
70 +
71 + /**
72 + * 获取待出库数量
73 + * @return 待出库数量
74 + */
75 + Integer getPendingDeliveryCount();
76 +
77 + /**
78 + * 获取最近的出库记录
79 + *
80 + * @param limit 限制数量
81 + * @return 最近出库列表
82 + */
83 + List<DeliveryRes> getRecentDeliveries(Integer limit);
70 } 84 }
71 85
72 86
......
...@@ -91,4 +91,12 @@ public interface ExceptionWorkorderService extends IService<ExceptionWorkorder> ...@@ -91,4 +91,12 @@ public interface ExceptionWorkorderService extends IService<ExceptionWorkorder>
91 * @return 统计信息 91 * @return 统计信息
92 */ 92 */
93 List<ExceptionWorkorderRes> getExceptionWorkorderStats(); 93 List<ExceptionWorkorderRes> getExceptionWorkorderStats();
94 +
95 + /**
96 + * 获取异常工单列表用于导出
97 + *
98 + * @param queryReq 查询条件
99 + * @return 异常工单列表(不分页)
100 + */
101 + List<ExceptionWorkorderRes> getExceptionWorkorderListForExport(ExceptionWorkorderQueryReq queryReq);
94 } 102 }
......
...@@ -67,6 +67,20 @@ public interface InvoiceMainService extends IService<InvoiceMain> { ...@@ -67,6 +67,20 @@ public interface InvoiceMainService extends IService<InvoiceMain> {
67 * @return 是否成功 67 * @return 是否成功
68 */ 68 */
69 boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus); 69 boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus);
70 +
71 + /**
72 + * 获取待开票数量
73 + * @return 待开票数量
74 + */
75 + Integer getPendingInvoiceCount();
76 +
77 + /**
78 + * 获取最近的发票记录
79 + *
80 + * @param limit 限制数量
81 + * @return 最近发票列表
82 + */
83 + List<InvoiceRes> getRecentInvoices(Integer limit);
70 } 84 }
71 85
72 86
......
...@@ -92,5 +92,29 @@ public interface OrderMainService extends IService<OrderMain> { ...@@ -92,5 +92,29 @@ public interface OrderMainService extends IService<OrderMain> {
92 * @return 是否成功 92 * @return 是否成功
93 */ 93 */
94 boolean updateRebateCalcFlag(Long orderId, Integer rebateCalcFlag); 94 boolean updateRebateCalcFlag(Long orderId, Integer rebateCalcFlag);
95 +
96 + /**
97 + * 获取今日订单数量
98 + *
99 + * @param date 日期
100 + * @return 订单数量
101 + */
102 + Integer getTodayOrderCount(String date);
103 +
104 + /**
105 + * 获取今日销售额
106 + *
107 + * @param date 日期
108 + * @return 销售额
109 + */
110 + Double getTodaySalesAmount(String date);
111 +
112 + /**
113 + * 获取最近的订单记录
114 + *
115 + * @param limit 限制数量
116 + * @return 最近订单列表
117 + */
118 + List<OrderRes> getRecentOrders(Integer limit);
95 } 119 }
96 120
......
...@@ -139,4 +139,12 @@ public interface RebateService extends IService<Rebate> { ...@@ -139,4 +139,12 @@ public interface RebateService extends IService<Rebate> {
139 * @return 趋势统计数据 139 * @return 趋势统计数据
140 */ 140 */
141 List<Map<String, Object>> getRebateTrendStats(String startDate, String endDate); 141 List<Map<String, Object>> getRebateTrendStats(String startDate, String endDate);
142 +
143 + /**
144 + * 获取最近的返利记录
145 + *
146 + * @param limit 限制数量
147 + * @return 最近返利列表
148 + */
149 + List<RebateRes> getRecentRebates(Integer limit);
142 } 150 }
......
1 +package com.apple.erp.service;
2 +
3 +import java.util.Map;
4 +
5 +/**
6 + * 会话管理服务接口
7 + *
8 + * @author Apple ERP Team
9 + * @version 1.0.0
10 + * @since 2024-01-01
11 + */
12 +public interface SessionService {
13 +
14 + /**
15 + * 存储用户会话信息
16 + * @param username 用户名
17 + * @param sessionData 会话数据
18 + */
19 + void storeUserSession(String username, Map<String, Object> sessionData);
20 +
21 + /**
22 + * 获取用户会话信息
23 + * @param username 用户名
24 + * @return 会话数据
25 + */
26 + Map<String, Object> getUserSession(String username);
27 +
28 + /**
29 + * 删除用户会话
30 + * @param username 用户名
31 + */
32 + void removeUserSession(String username);
33 +
34 + /**
35 + * 获取在线用户数
36 + * @return 在线用户数
37 + */
38 + Integer getOnlineUserCount();
39 +
40 + /**
41 + * 清理过期会话
42 + */
43 + void cleanupExpiredSessions();
44 +}
...@@ -22,6 +22,7 @@ import org.springframework.util.StringUtils; ...@@ -22,6 +22,7 @@ import org.springframework.util.StringUtils;
22 22
23 import java.time.LocalDateTime; 23 import java.time.LocalDateTime;
24 import java.time.format.DateTimeFormatter; 24 import java.time.format.DateTimeFormatter;
25 +import java.util.ArrayList;
25 import java.util.List; 26 import java.util.List;
26 import java.util.stream.Collectors; 27 import java.util.stream.Collectors;
27 28
...@@ -254,6 +255,34 @@ public class DeliveryMainServiceImpl extends ServiceImpl<DeliveryMainMapper, Del ...@@ -254,6 +255,34 @@ public class DeliveryMainServiceImpl extends ServiceImpl<DeliveryMainMapper, Del
254 return null; 255 return null;
255 } 256 }
256 } 257 }
258 +
259 + @Override
260 + public Integer getPendingDeliveryCount() {
261 + LambdaQueryWrapper<DeliveryMain> wrapper = new LambdaQueryWrapper<>();
262 + wrapper.eq(DeliveryMain::getDelFlag, "0")
263 + .eq(DeliveryMain::getDeliveryStatus, 0); // 0表示待出库
264 + return Math.toIntExact(count(wrapper));
265 + }
266 +
267 + @Override
268 + public List<DeliveryRes> getRecentDeliveries(Integer limit) {
269 + try {
270 + LambdaQueryWrapper<DeliveryMain> wrapper = new LambdaQueryWrapper<>();
271 + wrapper.eq(DeliveryMain::getDelFlag, "0")
272 + .orderByDesc(DeliveryMain::getCreateTime)
273 + .last("LIMIT " + (limit != null ? limit : 5));
274 +
275 + List<DeliveryMain> deliveries = list(wrapper);
276 + return deliveries.stream().map(delivery -> {
277 + DeliveryRes deliveryRes = new DeliveryRes();
278 + BeanUtils.copyProperties(delivery, deliveryRes);
279 + return deliveryRes;
280 + }).collect(Collectors.toList());
281 + } catch (Exception e) {
282 + log.error("获取最近出库记录失败", e);
283 + return new ArrayList<>();
284 + }
285 + }
257 } 286 }
258 287
259 288
......
...@@ -18,11 +18,9 @@ import org.springframework.beans.BeanUtils; ...@@ -18,11 +18,9 @@ import org.springframework.beans.BeanUtils;
18 import org.springframework.beans.factory.annotation.Autowired; 18 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
20 import org.springframework.transaction.annotation.Transactional; 20 import org.springframework.transaction.annotation.Transactional;
21 -import org.springframework.util.StringUtils;
22 21
23 import java.time.LocalDateTime; 22 import java.time.LocalDateTime;
24 import java.util.List; 23 import java.util.List;
25 -import java.util.stream.Collectors;
26 24
27 /** 25 /**
28 * 异常工单Service实现类 26 * 异常工单Service实现类
...@@ -200,4 +198,10 @@ public class ExceptionWorkorderServiceImpl extends ServiceImpl<ExceptionWorkorde ...@@ -200,4 +198,10 @@ public class ExceptionWorkorderServiceImpl extends ServiceImpl<ExceptionWorkorde
200 public List<ExceptionWorkorderRes> getExceptionWorkorderStats() { 198 public List<ExceptionWorkorderRes> getExceptionWorkorderStats() {
201 return exceptionWorkorderMapper.selectExceptionWorkorderStats(); 199 return exceptionWorkorderMapper.selectExceptionWorkorderStats();
202 } 200 }
201 +
202 + @Override
203 + public List<ExceptionWorkorderRes> getExceptionWorkorderListForExport(ExceptionWorkorderQueryReq queryReq) {
204 + // 不分页查询所有数据用于导出
205 + return exceptionWorkorderMapper.selectExceptionWorkorderListForExport(queryReq);
206 + }
203 } 207 }
......
...@@ -21,6 +21,7 @@ import org.springframework.util.StringUtils; ...@@ -21,6 +21,7 @@ import org.springframework.util.StringUtils;
21 import java.time.LocalDate; 21 import java.time.LocalDate;
22 import java.time.LocalDateTime; 22 import java.time.LocalDateTime;
23 import java.time.format.DateTimeFormatter; 23 import java.time.format.DateTimeFormatter;
24 +import java.util.ArrayList;
24 import java.util.List; 25 import java.util.List;
25 import java.util.stream.Collectors; 26 import java.util.stream.Collectors;
26 27
...@@ -229,4 +230,31 @@ public class InvoiceMainServiceImpl extends ServiceImpl<InvoiceMainMapper, Invoi ...@@ -229,4 +230,31 @@ public class InvoiceMainServiceImpl extends ServiceImpl<InvoiceMainMapper, Invoi
229 } 230 }
230 } 231 }
231 232
233 + @Override
234 + public Integer getPendingInvoiceCount() {
235 + LambdaQueryWrapper<InvoiceMain> wrapper = new LambdaQueryWrapper<>();
236 + wrapper.eq(InvoiceMain::getDelFlag, "0")
237 + .eq(InvoiceMain::getInvoiceStatus, 0); // 0表示待开票
238 + return Math.toIntExact(count(wrapper));
239 + }
240 +
241 + @Override
242 + public List<InvoiceRes> getRecentInvoices(Integer limit) {
243 + try {
244 + LambdaQueryWrapper<InvoiceMain> wrapper = new LambdaQueryWrapper<>();
245 + wrapper.eq(InvoiceMain::getDelFlag, "0")
246 + .orderByDesc(InvoiceMain::getCreateTime)
247 + .last("LIMIT " + (limit != null ? limit : 5));
248 +
249 + List<InvoiceMain> invoices = list(wrapper);
250 + return invoices.stream().map(invoice -> {
251 + InvoiceRes invoiceRes = new InvoiceRes();
252 + BeanUtils.copyProperties(invoice, invoiceRes);
253 + return invoiceRes;
254 + }).collect(Collectors.toList());
255 + } catch (Exception e) {
256 + log.error("获取最近发票记录失败", e);
257 + return new ArrayList<>();
258 + }
259 + }
232 } 260 }
......
...@@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional; ...@@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional;
16 import org.springframework.util.StringUtils; 16 import org.springframework.util.StringUtils;
17 17
18 import java.time.LocalDateTime; 18 import java.time.LocalDateTime;
19 +import java.util.ArrayList;
19 import java.util.List; 20 import java.util.List;
20 import java.util.stream.Collectors; 21 import java.util.stream.Collectors;
21 22
...@@ -288,5 +289,59 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain ...@@ -288,5 +289,59 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
288 order.setUpdateTime(LocalDateTime.now()); 289 order.setUpdateTime(LocalDateTime.now());
289 return orderMainMapper.updateById(order) > 0; 290 return orderMainMapper.updateById(order) > 0;
290 } 291 }
292 +
293 + @Override
294 + public Integer getTodayOrderCount(String date) {
295 + try {
296 + LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
297 + wrapper.eq(OrderMain::getDelFlag, "0")
298 + .like(OrderMain::getCreateTime, date);
299 + long count = count(wrapper);
300 + System.out.println("今日订单数量查询结果: " + count + ", 日期: " + date);
301 + return Math.toIntExact(count);
302 + } catch (Exception e) {
303 + System.err.println("查询今日订单数量失败: " + e.getMessage());
304 + return 0;
305 + }
306 + }
307 +
308 + @Override
309 + public Double getTodaySalesAmount(String date) {
310 + try {
311 + LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
312 + wrapper.eq(OrderMain::getDelFlag, "0")
313 + .like(OrderMain::getCreateTime, date);
314 +
315 + List<OrderMain> orders = list(wrapper);
316 + double totalAmount = orders.stream()
317 + .mapToDouble(order -> order.getTotalAmount() != null ? order.getTotalAmount().doubleValue() : 0.0)
318 + .sum();
319 + System.out.println("今日销售额查询结果: " + totalAmount + ", 日期: " + date);
320 + return totalAmount;
321 + } catch (Exception e) {
322 + System.err.println("查询今日销售额失败: " + e.getMessage());
323 + return 0.0;
324 + }
325 + }
326 +
327 + @Override
328 + public List<OrderRes> getRecentOrders(Integer limit) {
329 + try {
330 + LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
331 + wrapper.eq(OrderMain::getDelFlag, "0")
332 + .orderByDesc(OrderMain::getCreateTime)
333 + .last("LIMIT " + (limit != null ? limit : 5));
334 +
335 + List<OrderMain> orders = list(wrapper);
336 + return orders.stream().map(order -> {
337 + OrderRes orderRes = new OrderRes();
338 + BeanUtils.copyProperties(order, orderRes);
339 + return orderRes;
340 + }).collect(Collectors.toList());
341 + } catch (Exception e) {
342 + log.error("获取最近订单失败", e);
343 + return new ArrayList<>();
344 + }
345 + }
291 } 346 }
292 347
......
...@@ -275,4 +275,24 @@ public class RebateServiceImpl extends ServiceImpl<RebateMapper, Rebate> impleme ...@@ -275,4 +275,24 @@ public class RebateServiceImpl extends ServiceImpl<RebateMapper, Rebate> impleme
275 log.info("获取返利趋势统计数据,开始日期:{},结束日期:{}", startDate, endDate); 275 log.info("获取返利趋势统计数据,开始日期:{},结束日期:{}", startDate, endDate);
276 return rebateMapper.getRebateTrendStats(startDate, endDate); 276 return rebateMapper.getRebateTrendStats(startDate, endDate);
277 } 277 }
278 +
279 + @Override
280 + public List<RebateRes> getRecentRebates(Integer limit) {
281 + try {
282 + LambdaQueryWrapper<Rebate> wrapper = new LambdaQueryWrapper<>();
283 + wrapper.eq(Rebate::getDelFlag, "0")
284 + .orderByDesc(Rebate::getCreateTime)
285 + .last("LIMIT " + (limit != null ? limit : 5));
286 +
287 + List<Rebate> rebates = list(wrapper);
288 + return rebates.stream().map(rebate -> {
289 + RebateRes rebateRes = new RebateRes();
290 + BeanUtils.copyProperties(rebate, rebateRes);
291 + return rebateRes;
292 + }).collect(Collectors.toList());
293 + } catch (Exception e) {
294 + log.error("获取最近返利记录失败", e);
295 + return new ArrayList<>();
296 + }
297 + }
278 } 298 }
......
1 +package com.apple.erp.service.impl;
2 +
3 +import com.apple.erp.service.SessionService;
4 +import lombok.extern.slf4j.Slf4j;
5 +import org.springframework.beans.factory.annotation.Autowired;
6 +import org.springframework.data.redis.core.RedisTemplate;
7 +import org.springframework.scheduling.annotation.Scheduled;
8 +import org.springframework.stereotype.Service;
9 +
10 +import java.util.HashMap;
11 +import java.util.Map;
12 +import java.util.Set;
13 +
14 +/**
15 + * 会话管理服务实现
16 + *
17 + * @author Apple ERP Team
18 + * @version 1.0.0
19 + * @since 2024-01-01
20 + */
21 +@Slf4j
22 +@Service
23 +public class SessionServiceImpl implements SessionService {
24 +
25 + private static final String SESSION_KEY_PREFIX = "user:session:";
26 + private static final long SESSION_TIMEOUT = 1800; // 30分钟
27 +
28 + @Autowired
29 + private RedisTemplate<String, Object> redisTemplate;
30 +
31 + @Override
32 + public void storeUserSession(String username, Map<String, Object> sessionData) {
33 + try {
34 + String sessionKey = SESSION_KEY_PREFIX + username;
35 + redisTemplate.opsForValue().set(sessionKey, sessionData, SESSION_TIMEOUT);
36 + log.debug("用户会话已存储: {}", username);
37 + } catch (Exception e) {
38 + log.error("存储用户会话失败: {}", username, e);
39 + }
40 + }
41 +
42 + @Override
43 + public Map<String, Object> getUserSession(String username) {
44 + try {
45 + String sessionKey = SESSION_KEY_PREFIX + username;
46 + Object sessionData = redisTemplate.opsForValue().get(sessionKey);
47 + if (sessionData instanceof Map) {
48 + return (Map<String, Object>) sessionData;
49 + }
50 + } catch (Exception e) {
51 + log.error("获取用户会话失败: {}", username, e);
52 + }
53 + return new HashMap<>();
54 + }
55 +
56 + @Override
57 + public void removeUserSession(String username) {
58 + try {
59 + String sessionKey = SESSION_KEY_PREFIX + username;
60 + redisTemplate.delete(sessionKey);
61 + log.debug("用户会话已删除: {}", username);
62 + } catch (Exception e) {
63 + log.error("删除用户会话失败: {}", username, e);
64 + }
65 + }
66 +
67 + @Override
68 + public Integer getOnlineUserCount() {
69 + try {
70 + String pattern = SESSION_KEY_PREFIX + "*";
71 + Set<String> sessionKeys = redisTemplate.keys(pattern);
72 +
73 + if (sessionKeys != null) {
74 + int onlineCount = sessionKeys.size();
75 + log.debug("当前在线用户数: {}", onlineCount);
76 + return onlineCount;
77 + } else {
78 + log.warn("Redis中未找到用户会话数据");
79 + return 0;
80 + }
81 + } catch (Exception e) {
82 + log.error("获取在线用户数失败", e);
83 + return 0;
84 + }
85 + }
86 +
87 + @Override
88 + @Scheduled(fixedRate = 300000) // 每5分钟执行一次
89 + public void cleanupExpiredSessions() {
90 + try {
91 + String pattern = SESSION_KEY_PREFIX + "*";
92 + Set<String> sessionKeys = redisTemplate.keys(pattern);
93 +
94 + if (sessionKeys != null) {
95 + int cleanedCount = 0;
96 + for (String sessionKey : sessionKeys) {
97 + // Redis会自动清理过期的key,这里可以添加额外的清理逻辑
98 + // 比如检查会话的最后活动时间等
99 + cleanedCount++;
100 + }
101 + log.debug("会话清理完成,检查了 {} 个会话", cleanedCount);
102 + }
103 + } catch (Exception e) {
104 + log.error("清理过期会话失败", e);
105 + }
106 + }
107 +}
...@@ -11,7 +11,6 @@ import org.springframework.stereotype.Service; ...@@ -11,7 +11,6 @@ import org.springframework.stereotype.Service;
11 11
12 import java.util.ArrayList; 12 import java.util.ArrayList;
13 import java.util.List; 13 import java.util.List;
14 -import java.util.stream.Collectors;
15 14
16 /** 15 /**
17 * 系统菜单Service实现类 16 * 系统菜单Service实现类
...@@ -77,7 +76,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl ...@@ -77,7 +76,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
77 wrapper.eq(SysMenu::getDelFlag, "0"); 76 wrapper.eq(SysMenu::getDelFlag, "0");
78 77
79 // 排序:按父菜单ID和排序号升序 78 // 排序:按父菜单ID和排序号升序
80 - wrapper.orderByAsc(SysMenu::getParentId, SysMenu::getSort); 79 + wrapper.orderByAsc(SysMenu::getParentId).orderByAsc(SysMenu::getSort);
81 80
82 return wrapper; 81 return wrapper;
83 } 82 }
......
...@@ -198,33 +198,6 @@ public class DictValueConverter { ...@@ -198,33 +198,6 @@ public class DictValueConverter {
198 } 198 }
199 } 199 }
200 200
201 - /**
202 - * 加载指定字典类型到Redis
203 - */
204 - private static void loadDictToRedis(String dictType) {
205 - if (staticDictItemService == null) {
206 - return;
207 - }
208 -
209 - try {
210 - // 根据字典类型获取字典项
211 - List<SysDictItem> dictItems = staticDictItemService.getDictItemsByType(dictType);
212 -
213 - Map<String, String> mapping = new HashMap<>();
214 - for (SysDictItem item : dictItems) {
215 - if (item.getDelFlag() == null || "0".equals(item.getDelFlag())) {
216 - mapping.put(item.getDictValue(), item.getDictLabel());
217 - }
218 - }
219 -
220 - // 存储到Redis
221 - String cacheKey = DICT_CACHE_PREFIX + dictType;
222 - staticRedisTemplate.opsForValue().set(cacheKey, mapping, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
223 -
224 - } catch (Exception e) {
225 - System.err.println("加载字典到Redis失败: " + e.getMessage());
226 - }
227 - }
228 201
229 /** 202 /**
230 * 根据字典类型ID获取字典类型编码 203 * 根据字典类型ID获取字典类型编码
......
...@@ -150,4 +150,76 @@ ...@@ -150,4 +150,76 @@
150 AND del_flag = '0' 150 AND del_flag = '0'
151 </update> 151 </update>
152 152
153 + <!-- 获取异常工单列表用于导出 -->
154 + <select id="selectExceptionWorkorderListForExport" resultType="com.apple.erp.dto.ExceptionWorkorderRes">
155 + SELECT
156 + ew.workorder_id,
157 + ew.workorder_no,
158 + ew.order_no,
159 + ew.dealer_code,
160 + ew.dealer_name,
161 + ew.exception_type,
162 + CASE ew.exception_type
163 + WHEN 1 THEN '逻辑验证异常'
164 + WHEN 2 THEN '源头验证异常'
165 + WHEN 3 THEN '交叉验证异常'
166 + ELSE '未知类型'
167 + END AS exception_type_name,
168 + ew.severity_level,
169 + CASE ew.severity_level
170 + WHEN 1 THEN '高'
171 + WHEN 2 THEN '中'
172 + WHEN 3 THEN '低'
173 + ELSE '未知'
174 + END AS severity_level_name,
175 + ew.workorder_status,
176 + CASE ew.workorder_status
177 + WHEN 1 THEN '待处理'
178 + WHEN 2 THEN '处理中'
179 + WHEN 3 THEN '已解决'
180 + WHEN 4 THEN '已关闭'
181 + ELSE '未知状态'
182 + END AS workorder_status_name,
183 + ew.exception_desc,
184 + ew.handler_user,
185 + ew.create_by,
186 + ew.create_time,
187 + ew.update_time
188 + FROM t_exception_workorder ew
189 + <where>
190 + ew.del_flag = '0'
191 + <if test="query.workorderNo != null and query.workorderNo != ''">
192 + AND ew.workorder_no LIKE CONCAT('%', #{query.workorderNo}, '%')
193 + </if>
194 + <if test="query.orderNo != null and query.orderNo != ''">
195 + AND ew.order_no LIKE CONCAT('%', #{query.orderNo}, '%')
196 + </if>
197 + <if test="query.dealerCode != null and query.dealerCode != ''">
198 + AND ew.dealer_code = #{query.dealerCode}
199 + </if>
200 + <if test="query.dealerName != null and query.dealerName != ''">
201 + AND ew.dealer_name LIKE CONCAT('%', #{query.dealerName}, '%')
202 + </if>
203 + <if test="query.workorderStatus != null">
204 + AND ew.workorder_status = #{query.workorderStatus}
205 + </if>
206 + <if test="query.exceptionType != null">
207 + AND ew.exception_type = #{query.exceptionType}
208 + </if>
209 + <if test="query.severityLevel != null">
210 + AND ew.severity_level = #{query.severityLevel}
211 + </if>
212 + <if test="query.handlerUser != null and query.handlerUser != ''">
213 + AND ew.handler_user LIKE CONCAT('%', #{query.handlerUser}, '%')
214 + </if>
215 + <if test="query.startTime != null">
216 + AND ew.create_time >= #{query.startTime}
217 + </if>
218 + <if test="query.endTime != null">
219 + AND ew.create_time &lt;= #{query.endTime}
220 + </if>
221 + </where>
222 + ORDER BY ew.create_time DESC
223 + </select>
224 +
153 </mapper> 225 </mapper>
......
1 +import { request } from '@/utils/request'
2 +
3 +/**
4 + * 首页统计数据响应接口
5 + */
6 +export interface DashboardStats {
7 + todayOrderCount: number
8 + pendingDeliveryCount: number
9 + pendingInvoiceCount: number
10 + todaySalesAmount: number
11 + onlineUserCount: number
12 + systemStatus: string
13 + systemVersion: string
14 + orderGrowthRate: string
15 + salesGrowthRate: string
16 +}
17 +
18 +export interface RecentActivity {
19 + id: number
20 + type: string
21 + title: string
22 + time: string
23 +}
24 +
25 +/**
26 + * 首页API
27 + */
28 +export const dashboardApi = {
29 + /**
30 + * 获取首页统计数据
31 + */
32 + getDashboardStats: (): Promise<DashboardStats> => {
33 + return request.get('/api/dashboard/stats')
34 + },
35 +
36 + /**
37 + * 获取最近活动
38 + */
39 + getRecentActivities: (): Promise<RecentActivity[]> => {
40 + return request.get('/api/dashboard/activities')
41 + },
42 +
43 + /**
44 + * 获取测试数据
45 + */
46 + getTestStats: (): Promise<DashboardStats> => {
47 + return request.get('/api/dashboard/test')
48 + }
49 +}
50 +
51 +export default dashboardApi
...@@ -146,6 +146,13 @@ export const exceptionWorkorderApi = { ...@@ -146,6 +146,13 @@ export const exceptionWorkorderApi = {
146 // 获取异常工单统计信息 146 // 获取异常工单统计信息
147 getExceptionWorkorderStats: () => { 147 getExceptionWorkorderStats: () => {
148 return request.get('/api/exception-workorder/stats') 148 return request.get('/api/exception-workorder/stats')
149 + },
150 +
151 + // 导出异常工单数据
152 + exportExceptionWorkorders: (params: ExceptionWorkorderQueryReq) => {
153 + return request.post('/api/exception-workorder/export', params, {
154 + responseType: 'blob'
155 + })
149 } 156 }
150 } 157 }
151 158
......
...@@ -32,6 +32,8 @@ export interface RebateSearchParams { ...@@ -32,6 +32,8 @@ export interface RebateSearchParams {
32 productCode?: string 32 productCode?: string
33 operateType?: number 33 operateType?: number
34 calcFlag?: number 34 calcFlag?: number
35 + auditStatus?: number
36 + dataSource?: string
35 rebateStartDate?: string 37 rebateStartDate?: string
36 rebateEndDate?: string 38 rebateEndDate?: string
37 } 39 }
......
This diff is collapsed. Click to expand it.
...@@ -734,8 +734,38 @@ const handleBatchProcess = () => { ...@@ -734,8 +734,38 @@ const handleBatchProcess = () => {
734 } 734 }
735 735
736 // 导出 736 // 导出
737 -const handleExport = () => { 737 +const handleExport = async () => {
738 - ElMessage.info('导出功能开发中...') 738 + try {
739 + await ElMessageBox.confirm('确定要导出异常工单数据吗?', '确认导出', {
740 + confirmButtonText: '确定',
741 + cancelButtonText: '取消',
742 + type: 'warning'
743 + })
744 +
745 + ElMessage.info('正在导出数据,请稍候...')
746 +
747 + const response = await exceptionWorkorderApi.exportExceptionWorkorders(searchParams.value)
748 +
749 + // 创建下载链接
750 + const blob = new Blob([response as unknown as BlobPart], {
751 + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
752 + })
753 + const url = window.URL.createObjectURL(blob)
754 + const link = document.createElement('a')
755 + link.href = url
756 + link.download = `异常工单数据_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.xlsx`
757 + document.body.appendChild(link)
758 + link.click()
759 + document.body.removeChild(link)
760 + window.URL.revokeObjectURL(url)
761 +
762 + ElMessage.success('导出成功')
763 + } catch (error: any) {
764 + if (error !== 'cancel') {
765 + console.error('导出失败:', error)
766 + ElMessage.error('导出失败: ' + (error?.message || '未知错误'))
767 + }
768 + }
739 } 769 }
740 770
741 // 提交新增 771 // 提交新增
......
This diff is collapsed. Click to expand it.