19 changed files with 1243 additions and 571 deletions
-
5pom.xml
-
6src/main/java/com/example/demo/DemoApplication.java
-
96src/main/java/com/example/demo/Node/RefundApprovalNode.java
-
37src/main/java/com/example/demo/config/FlowableDataSourceConfig.java
-
70src/main/java/com/example/demo/config/cash/DataInitializer.java
-
92src/main/java/com/example/demo/config/cash/RefundApprovalFlowConfig.java
-
69src/main/java/com/example/demo/config/work/FlowableEngineConfig.java
-
310src/main/java/com/example/demo/controller/cash/CashRefundController.java
-
8src/main/java/com/example/demo/domain/entity/User.java
-
244src/main/java/com/example/demo/domain/vo/cash/CashRecord.java
-
43src/main/java/com/example/demo/domain/vo/cash/Market.java
-
43src/main/java/com/example/demo/domain/vo/cash/RefundApprovalRecord.java
-
84src/main/java/com/example/demo/domain/vo/cash/User.java
-
173src/main/java/com/example/demo/service/cash/MarketService.java
-
221src/main/java/com/example/demo/service/cash/ProcessService.java
-
286src/main/java/com/example/demo/service/cash/RefundApprovalService.java
-
9src/main/resources/application-dev.yml
-
13src/main/resources/application-test.yml
-
5src/main/resources/cashMapper/CashRecordMapper.xml
@ -0,0 +1,96 @@ |
|||||
|
package com.example.demo.Node; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import java.util.HashSet; |
||||
|
import java.util.Set; |
||||
|
|
||||
|
@Data |
||||
|
public class RefundApprovalNode { |
||||
|
private String nodeName; |
||||
|
private Integer level; |
||||
|
private Set<String> roles; // 审批所需的角色集合 |
||||
|
private Set<Integer> allowedMarketIds; // 允许审批的地区ID集合 |
||||
|
private Set<String> allowedMarketNames; // 允许审批的地区名称集合(向后兼容) |
||||
|
private boolean required; |
||||
|
|
||||
|
public RefundApprovalNode() { |
||||
|
this.roles = new HashSet<>(); |
||||
|
this.allowedMarketIds = new HashSet<>(); |
||||
|
this.allowedMarketNames = new HashSet<>(); |
||||
|
} |
||||
|
|
||||
|
public RefundApprovalNode(String nodeName, Integer level, String role, boolean required) { |
||||
|
this(); |
||||
|
this.nodeName = nodeName; |
||||
|
this.level = level; |
||||
|
if (role != null) { |
||||
|
this.roles.add(role); |
||||
|
} |
||||
|
this.required = required; |
||||
|
} |
||||
|
|
||||
|
public RefundApprovalNode(String nodeName, Integer level, Set<String> roles, Set<Integer> allowedMarketIds, boolean required) { |
||||
|
this.nodeName = nodeName; |
||||
|
this.level = level; |
||||
|
this.roles = roles != null ? roles : new HashSet<>(); |
||||
|
this.allowedMarketIds = allowedMarketIds != null ? allowedMarketIds : new HashSet<>(); |
||||
|
this.allowedMarketNames = new HashSet<>(); |
||||
|
this.required = required; |
||||
|
} |
||||
|
// 构造函数:接受多个角色(数组形式)和地区ID集合 |
||||
|
public RefundApprovalNode(String nodeName, Integer level, String[] roles, Set<Integer> allowedMarketIds, boolean required) { |
||||
|
this(); |
||||
|
this.nodeName = nodeName; |
||||
|
this.level = level; |
||||
|
if (roles != null) { |
||||
|
for (String role : roles) { |
||||
|
this.roles.add(role); |
||||
|
} |
||||
|
} |
||||
|
this.allowedMarketIds = allowedMarketIds != null ? allowedMarketIds : new HashSet<>(); |
||||
|
this.required = required; |
||||
|
} |
||||
|
|
||||
|
public void addRole(String role) { |
||||
|
if (this.roles == null) { |
||||
|
this.roles = new HashSet<>(); |
||||
|
} |
||||
|
this.roles.add(role); |
||||
|
} |
||||
|
|
||||
|
public void addMarketId(Integer marketId) { |
||||
|
if (this.allowedMarketIds == null) { |
||||
|
this.allowedMarketIds = new HashSet<>(); |
||||
|
} |
||||
|
this.allowedMarketIds.add(marketId); |
||||
|
} |
||||
|
|
||||
|
public void addMarketName(String marketName) { |
||||
|
if (this.allowedMarketNames == null) { |
||||
|
this.allowedMarketNames = new HashSet<>(); |
||||
|
} |
||||
|
this.allowedMarketNames.add(marketName); |
||||
|
} |
||||
|
|
||||
|
// 检查节点是否对指定地区ID可见 |
||||
|
public boolean isMarketAllowed(Integer marketId) { |
||||
|
if (allowedMarketIds == null || allowedMarketIds.isEmpty()) { |
||||
|
return true; // 没有地区限制,所有地区都可见 |
||||
|
} |
||||
|
return allowedMarketIds.contains(marketId); |
||||
|
} |
||||
|
|
||||
|
// 检查节点是否对指定地区名称可见(向后兼容) |
||||
|
public boolean isMarketAllowedByName(String marketName) { |
||||
|
if ((allowedMarketNames == null || allowedMarketNames.isEmpty()) && |
||||
|
(allowedMarketIds == null || allowedMarketIds.isEmpty())) { |
||||
|
return true; // 没有地区限制,所有地区都可见 |
||||
|
} |
||||
|
|
||||
|
if (allowedMarketNames != null && !allowedMarketNames.isEmpty()) { |
||||
|
return allowedMarketNames.contains(marketName); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
@ -1,37 +0,0 @@ |
|||||
// FlowableDataSourceConfig.java |
|
||||
package com.example.demo.config; |
|
||||
|
|
||||
import com.zaxxer.hikari.HikariDataSource; |
|
||||
import org.springframework.beans.factory.annotation.Qualifier; |
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder; |
|
||||
import org.springframework.context.annotation.Bean; |
|
||||
import org.springframework.context.annotation.Configuration; |
|
||||
import org.springframework.context.annotation.Primary; |
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager; |
|
||||
import org.springframework.transaction.PlatformTransactionManager; |
|
||||
|
|
||||
import javax.sql.DataSource; |
|
||||
|
|
||||
@Configuration |
|
||||
public class FlowableDataSourceConfig { |
|
||||
|
|
||||
@Bean(name = "flowableDataSource") |
|
||||
@Primary |
|
||||
@ConfigurationProperties(prefix = "spring.datasource.flowable") |
|
||||
public DataSource flowableDataSource() { |
|
||||
HikariDataSource ds = DataSourceBuilder |
|
||||
.create() |
|
||||
.type(HikariDataSource.class) |
|
||||
.build(); |
|
||||
ds.setConnectionInitSql("SET SESSION sql_mode='TRADITIONAL';"); |
|
||||
return ds; |
|
||||
} |
|
||||
|
|
||||
@Bean(name = "flowableTransactionManager") |
|
||||
@Primary |
|
||||
public PlatformTransactionManager flowableTransactionManager( |
|
||||
@Qualifier("flowableDataSource") DataSource dataSource) { |
|
||||
return new DataSourceTransactionManager(dataSource); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,70 @@ |
|||||
|
package com.example.demo.config.cash; |
||||
|
|
||||
|
|
||||
|
|
||||
|
import com.example.demo.domain.vo.cash.Market; |
||||
|
import com.example.demo.service.cash.MarketService; |
||||
|
import com.example.demo.service.cash.RefundApprovalService; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.boot.CommandLineRunner; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Component |
||||
|
@RequiredArgsConstructor |
||||
|
public class DataInitializer implements CommandLineRunner { |
||||
|
|
||||
|
private final MarketService marketService; |
||||
|
private final RefundApprovalService refundApprovalService; |
||||
|
|
||||
|
@Override |
||||
|
public void run(String... args) throws Exception { |
||||
|
// 初始化地区数据 |
||||
|
initializeMarketData(); |
||||
|
} |
||||
|
|
||||
|
private void initializeMarketData() { |
||||
|
List<Market> markets = Arrays.asList( |
||||
|
createMarket(1, "Capt", -1, "Capt-Capt-Capt-Capt", 0), |
||||
|
createMarket(2, "公司", 1, "Capt-公司-公司-公司", 1), |
||||
|
createMarket(3, "市场", 1, "Capt-市场部-市场部-市场部", 1), |
||||
|
createMarket(4, "新加坡", 3, "Capt-市场部-新加坡-新加坡", 2), |
||||
|
createMarket(5, "马来西亚", 3, "Capt-市场部-马来西亚-马来西亚", 2), |
||||
|
createMarket(9, "研发部", 2, "Capt-公司-研发部-研发部", 2), |
||||
|
createMarket(13, "香港", 3, "Capt-市场部-香港-香港", 2), |
||||
|
createMarket(9999, "总部", 2, "Capt-公司-总部-总部", 2), |
||||
|
createMarket(24016, "加拿大", 3, "Capt-市场部-加拿大-加拿大", 2), |
||||
|
createMarket(24018, "泰国", 3, "Capt-市场部-泰国-泰国", 2), |
||||
|
createMarket(24022, "越南HCM", 3, "Capt-市场部-越南HCM-越南HCM", 2), |
||||
|
createMarket(24027, "韩国", 3, "Capt-市场部-韩国-韩国", 2), |
||||
|
createMarket(24028, "深圳运营", 3, "Capt-市场部-深圳运营-深圳运营", 2), |
||||
|
createMarket(24030, "未知", 3, "Capt-市场部-未知", 2), |
||||
|
createMarket(24031, "其他", 3, "Capt-市场部-其他", 2), |
||||
|
createMarket(24032, "市场部", 3, "Capt-市场部-市场部", 2) |
||||
|
); |
||||
|
|
||||
|
marketService.initializeMarkets(markets); |
||||
|
|
||||
|
// 为特定市场初始化审批流程 |
||||
|
refundApprovalService.initializeFlowForMarketName("新加坡"); |
||||
|
refundApprovalService.initializeFlowForMarketName("马来西亚"); |
||||
|
refundApprovalService.initializeFlowForMarketName("香港"); |
||||
|
refundApprovalService.initializeFlowForMarketName("加拿大"); |
||||
|
refundApprovalService.initializeFlowForMarketName("泰国"); |
||||
|
refundApprovalService.initializeFlowForMarketName("越南HCM"); |
||||
|
refundApprovalService.initializeFlowForMarketName("韩国"); |
||||
|
refundApprovalService.initializeFlowForMarketName("深圳运营"); |
||||
|
} |
||||
|
|
||||
|
private Market createMarket(Integer id, String name, Integer parentId, String treelist, Integer type) { |
||||
|
Market market = new Market(); |
||||
|
market.setId(id); |
||||
|
market.setName(name); |
||||
|
market.setParentId(parentId); |
||||
|
market.setTreelist(treelist); |
||||
|
market.setType(type); |
||||
|
return market; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
package com.example.demo.config.cash; |
||||
|
|
||||
|
import com.example.demo.Node.RefundApprovalNode; |
||||
|
import com.example.demo.domain.vo.cash.User; |
||||
|
import lombok.Data; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class RefundApprovalFlowConfig { |
||||
|
private String flowName; |
||||
|
private List<RefundApprovalNode> nodes; |
||||
|
|
||||
|
public RefundApprovalFlowConfig() { |
||||
|
this.nodes = new ArrayList<>(); |
||||
|
} |
||||
|
|
||||
|
public RefundApprovalFlowConfig(String flowName, List<RefundApprovalNode> nodes) { |
||||
|
this.flowName = flowName; |
||||
|
this.nodes = nodes != null ? nodes : new ArrayList<>(); |
||||
|
} |
||||
|
|
||||
|
// 获取用户可见的状态列表(基于角色和地区ID权限) |
||||
|
public List<Integer> getVisibleStatuses(User user, Integer orderMarketId) { |
||||
|
List<Integer> visibleStatuses = new ArrayList<>(); |
||||
|
|
||||
|
for (RefundApprovalNode node : nodes) { |
||||
|
if (isNodeVisibleToUser(node, user, orderMarketId)) { |
||||
|
visibleStatuses.add(node.getLevel()); |
||||
|
} |
||||
|
} |
||||
|
return visibleStatuses; |
||||
|
} |
||||
|
|
||||
|
// 判断节点对用户是否可见(基于角色和地区ID权限) |
||||
|
public boolean isNodeVisibleToUser(RefundApprovalNode node, User user, Integer orderMarketId) { |
||||
|
if (node == null || user == null) return false; |
||||
|
|
||||
|
// 检查角色权限 |
||||
|
boolean hasRolePermission = true; |
||||
|
if (node.getRoles() != null && !node.getRoles().isEmpty()) { |
||||
|
hasRolePermission = user.hasAnyRole(node.getRoles()); |
||||
|
} |
||||
|
|
||||
|
// 检查地区权限 |
||||
|
boolean hasMarketPermission = true; |
||||
|
if (node.getAllowedMarketIds() != null && !node.getAllowedMarketIds().isEmpty()) { |
||||
|
// 如果节点限制了地区,用户需要在这些地区有权限 |
||||
|
hasMarketPermission = user.canApproveAnyMarket(node.getAllowedMarketIds()); |
||||
|
} else if (orderMarketId != null) { |
||||
|
// 如果节点没有限制地区,但订单有地区,用户需要在该订单地区有权限 |
||||
|
hasMarketPermission = user.canApproveMarket(orderMarketId); |
||||
|
} |
||||
|
|
||||
|
return hasRolePermission && hasMarketPermission; |
||||
|
} |
||||
|
|
||||
|
// 判断用户是否可以审批某个节点的订单 |
||||
|
public boolean canUserApproveNode(User user, Integer level, Integer orderMarketId) { |
||||
|
RefundApprovalNode node = findNodeByLevel(level); |
||||
|
if (node == null) return false; |
||||
|
|
||||
|
return isNodeVisibleToUser(node, user, orderMarketId); |
||||
|
} |
||||
|
|
||||
|
// 根据级别查找节点 |
||||
|
public RefundApprovalNode findNodeByLevel(Integer level) { |
||||
|
if (nodes == null) return null; |
||||
|
return nodes.stream() |
||||
|
.filter(node -> level.equals(node.getLevel())) |
||||
|
.findFirst() |
||||
|
.orElse(null); |
||||
|
} |
||||
|
|
||||
|
// 获取下一个审批节点 |
||||
|
public RefundApprovalNode getNextNode(Integer currentLevel) { |
||||
|
if (nodes == null || nodes.isEmpty()) return null; |
||||
|
|
||||
|
return nodes.stream() |
||||
|
.filter(node -> node.getLevel() > currentLevel) |
||||
|
.min((a, b) -> a.getLevel().compareTo(b.getLevel())) |
||||
|
.orElse(null); |
||||
|
} |
||||
|
|
||||
|
// 获取所有涉及的市场ID |
||||
|
public java.util.Set<Integer> getAllInvolvedMarketIds() { |
||||
|
return nodes.stream() |
||||
|
.filter(node -> node.getAllowedMarketIds() != null) |
||||
|
.flatMap(node -> node.getAllowedMarketIds().stream()) |
||||
|
.collect(java.util.stream.Collectors.toSet()); |
||||
|
} |
||||
|
} |
||||
@ -1,69 +0,0 @@ |
|||||
package com.example.demo.config.work; |
|
||||
|
|
||||
import org.flowable.common.engine.impl.history.HistoryLevel; |
|
||||
import org.flowable.engine.*; |
|
||||
import org.flowable.spring.ProcessEngineFactoryBean; |
|
||||
import org.flowable.spring.SpringProcessEngineConfiguration; |
|
||||
import org.springframework.beans.factory.annotation.Qualifier; |
|
||||
import org.springframework.context.annotation.Bean; |
|
||||
import org.springframework.context.annotation.Configuration; |
|
||||
import org.springframework.transaction.PlatformTransactionManager; |
|
||||
|
|
||||
import javax.sql.DataSource; |
|
||||
|
|
||||
@Configuration |
|
||||
public class FlowableEngineConfig { |
|
||||
|
|
||||
/** |
|
||||
* 1. 创建 ProcessEngineFactoryBean |
|
||||
* Spring 会负责调用 buildProcessEngine() 并缓存实例 |
|
||||
*/ |
|
||||
@Bean |
|
||||
public ProcessEngineFactoryBean processEngineFactory( |
|
||||
@Qualifier("flowableDataSource") DataSource dataSource, |
|
||||
@Qualifier("flowableTransactionManager") PlatformTransactionManager transactionManager) { |
|
||||
|
|
||||
SpringProcessEngineConfiguration cfg = new SpringProcessEngineConfiguration(); |
|
||||
cfg.setDataSource(dataSource); |
|
||||
cfg.setTransactionManager(transactionManager); |
|
||||
cfg.setDatabaseSchemaUpdate("false"); // 自动建/升级表 |
|
||||
cfg.setHistoryLevel(HistoryLevel.FULL); // 记录完整历史 |
|
||||
cfg.setAsyncExecutorActivate(false); // 暂不开启异步执行器 |
|
||||
|
|
||||
ProcessEngineFactoryBean factory = new ProcessEngineFactoryBean(); |
|
||||
factory.setProcessEngineConfiguration(cfg); |
|
||||
return factory; |
|
||||
} |
|
||||
|
|
||||
/* 2. 各类 Service 注入 —— 都取自同一个 ProcessEngine *********************/ |
|
||||
|
|
||||
@Bean |
|
||||
public RepositoryService repositoryService(ProcessEngine processEngine) { |
|
||||
return processEngine.getRepositoryService(); |
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
public RuntimeService runtimeService(ProcessEngine processEngine) { |
|
||||
return processEngine.getRuntimeService(); |
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
public TaskService taskService(ProcessEngine processEngine) { |
|
||||
return processEngine.getTaskService(); |
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
public HistoryService historyService(ProcessEngine processEngine) { |
|
||||
return processEngine.getHistoryService(); |
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
public ManagementService managementService(ProcessEngine processEngine) { |
|
||||
return processEngine.getManagementService(); |
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
public IdentityService identityService(ProcessEngine processEngine) { |
|
||||
return processEngine.getIdentityService(); |
|
||||
} |
|
||||
} |
|
||||
@ -1,215 +1,95 @@ |
|||||
//package com.example.demo.controller.cash; |
|
||||
// |
|
||||
// |
|
||||
//import com.example.demo.Util.JWTUtil; |
|
||||
//import com.example.demo.domain.entity.Admin; |
|
||||
//import com.example.demo.domain.vo.cash.CashCollection; |
|
||||
//import com.example.demo.domain.vo.cash.CashRecordDone; |
|
||||
//import com.example.demo.domain.vo.coin.Page; |
|
||||
//import com.example.demo.domain.vo.coin.Result; |
|
||||
//import com.example.demo.service.cash.ProcessService; |
|
||||
//import com.example.demo.service.coin.MarketService; |
|
||||
//import com.example.demo.serviceImpl.cash.CashRefundServiceImpl; |
|
||||
//import com.github.pagehelper.PageInfo; |
|
||||
//import jakarta.annotation.Resource; |
|
||||
//import jakarta.servlet.http.HttpServletRequest; |
|
||||
//import lombok.RequiredArgsConstructor; |
|
||||
//import lombok.extern.slf4j.Slf4j; |
|
||||
//import org.apache.commons.lang3.StringUtils; |
|
||||
//import org.flowable.engine.ProcessEngine; |
|
||||
//import org.flowable.engine.RepositoryService; |
|
||||
//import org.flowable.engine.RuntimeService; |
|
||||
//import org.flowable.engine.TaskService; |
|
||||
//import org.flowable.task.api.Task; |
|
||||
//import org.springframework.beans.factory.annotation.Autowired; |
|
||||
//import org.springframework.util.ObjectUtils; |
|
||||
//import org.springframework.web.bind.annotation.*; |
|
||||
//import org.springframework.web.context.request.RequestContextHolder; |
|
||||
//import org.springframework.web.context.request.ServletRequestAttributes; |
|
||||
// |
|
||||
//import java.util.Arrays; |
|
||||
//import java.util.HashMap; |
|
||||
//import java.util.List; |
|
||||
//import java.util.Map; |
|
||||
// |
|
||||
///** |
|
||||
// * @program: GOLD |
|
||||
// * @ClassName RefundController |
|
||||
// * @description: |
|
||||
// * @author: huangqizhen |
|
||||
// * @create: 2025−09-26 14:15 |
|
||||
// * @Version 1.0 |
|
||||
// **/ |
|
||||
//@RestController |
|
||||
//@RequestMapping("/Money") |
|
||||
//@RequiredArgsConstructor |
|
||||
//@Slf4j |
|
||||
//@CrossOrigin |
|
||||
//public class CashRefundController { |
|
||||
// @Autowired |
|
||||
// private CashRefundServiceImpl cashRefundServiceImpl; |
|
||||
// @Autowired |
|
||||
// MarketService marketService; |
|
||||
// @Resource |
|
||||
// private RepositoryService repositoryService; |
|
||||
// @Resource |
|
||||
// private RuntimeService runtimeService; |
|
||||
// @Resource |
|
||||
// private TaskService taskService; |
|
||||
// @Resource |
|
||||
// private ProcessEngine processEngine; |
|
||||
// @Autowired |
|
||||
// private ProcessService processService; |
|
||||
// @PostMapping("/select") |
|
||||
// public Result select(@RequestBody Page page) throws Exception { |
|
||||
// // 获取当前请求对象 |
|
||||
// HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); |
|
||||
// String token = request.getHeader("token"); |
|
||||
// |
|
||||
//// 解析 token 获取用户信息 |
|
||||
// Admin admin = (Admin) JWTUtil.getUserDetailsList(String.valueOf(token), Admin.class); |
|
||||
// List<String> userMarkets = Arrays.asList(StringUtils.split(admin.getMarkets(), ",")); |
|
||||
// List<String> markets = marketService.getMarketIds(userMarkets); |
|
||||
// |
|
||||
//// 校验分页参数 |
|
||||
// if (ObjectUtils.isEmpty(page.getPageNum())) { |
|
||||
// return Result.error("页码数为空!"); |
|
||||
// } |
|
||||
// if (ObjectUtils.isEmpty(page.getPageSize())) { |
|
||||
// return Result.error("页大小为空!"); |
|
||||
// } |
|
||||
// |
|
||||
//// 获取传入的市场列表 |
|
||||
// List<String> requestedMarkets = page.getCashRecordDone() != null ? page.getCashRecordDone().getMarkets() : null; |
|
||||
// |
|
||||
//// 权限校验逻辑 |
|
||||
// if (markets.contains("9") || markets.contains("9999")) { |
|
||||
// // 特权市场:9 或 9999,跳过权限校验,直接放行传入的 markets |
|
||||
// // 如果业务需要,也可以在这里做空值处理 |
|
||||
// if (page.getCashRecordDone() != null) { |
|
||||
// // 保持 requestedMarkets 不变,原样接受 |
|
||||
// // 可选:如果 requestedMarkets 为 null,可设为默认值或保持 null |
|
||||
// } |
|
||||
// } else { |
|
||||
// // 普通用户:必须校验权限 |
|
||||
// if (requestedMarkets == null || requestedMarkets.isEmpty()) { |
|
||||
// page.getCashRecordDone().setMarkets(requestedMarkets); |
|
||||
// } |
|
||||
// if (!markets.containsAll(requestedMarkets)) { |
|
||||
// return Result.error("无权限!请求的市场不在授权范围内。"); |
|
||||
// } |
|
||||
// // 校验通过,保持 requestedMarkets 不变 |
|
||||
// } |
|
||||
// return Result.success(cashRefundServiceImpl.select(page.getPageNum(),page.getPageSize(),page.getCashRecordDone())); |
|
||||
// } |
|
||||
// @PostMapping("/add") |
|
||||
// public Result add(@RequestBody CashRecordDone cashCollection){ |
|
||||
// return Result.success(cashRefundServiceImpl.add(cashCollection)); |
|
||||
// } |
|
||||
// @PostMapping("/update") |
|
||||
// public Result update(@RequestBody CashRecordDone cashRecordDone){ |
|
||||
// return Result.success(cashRefundServiceImpl.update(cashRecordDone)); |
|
||||
// } |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// /** |
|
||||
// * 当地财务审核 |
|
||||
// */ |
|
||||
// @PostMapping("/local-finance/approve") |
|
||||
// public Map<String, Object> localFinanceApprove( |
|
||||
// @RequestParam String taskId, |
|
||||
// @RequestParam boolean approved, |
|
||||
// @RequestParam(required = false) String comment) { |
|
||||
// |
|
||||
// processService.localFinanceApprove(taskId, approved, comment); |
|
||||
// |
|
||||
// Map<String, Object> result = new HashMap<>(); |
|
||||
// result.put("success", true); |
|
||||
// result.put("message", approved ? "当地财务审核通过" : "当地财务已驳回"); |
|
||||
// |
|
||||
// return result; |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 地区负责人审核 |
|
||||
// */ |
|
||||
// @PostMapping("/regional-manager/approve") |
|
||||
// public Map<String, Object> regionalManagerApprove( |
|
||||
// @RequestParam String taskId, |
|
||||
// @RequestParam boolean approved, |
|
||||
// @RequestParam(required = false) String comment) { |
|
||||
// |
|
||||
// processService.regionalManagerApprove(taskId, approved, comment); |
|
||||
// |
|
||||
// Map<String, Object> result = new HashMap<>(); |
|
||||
// result.put("success", true); |
|
||||
// result.put("message", approved ? "地区负责人审核通过" : "地区负责人已驳回"); |
|
||||
// |
|
||||
// return result; |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 总部财务审批 |
|
||||
// */ |
|
||||
// @PostMapping("/head-finance/approve") |
|
||||
// public Map<String, Object> headFinanceApprove( |
|
||||
// @RequestParam String taskId, |
|
||||
// @RequestParam boolean approved, |
|
||||
// @RequestParam(required = false) String comment) { |
|
||||
// |
|
||||
// processService.headFinanceApprove(taskId, approved, comment); |
|
||||
// |
|
||||
// Map<String, Object> result = new HashMap<>(); |
|
||||
// result.put("success", true); |
|
||||
// result.put("message", approved ? "总部财务审批通过" : "总部财务已驳回"); |
|
||||
// |
|
||||
// return result; |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 提交审批申请 |
|
||||
// */ |
|
||||
// @PostMapping("/submit") |
|
||||
// public Map<String, Object> submitApproval(@RequestBody CashRecordDone order) { |
|
||||
// return processService.submitApproval(order); |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 获取待办审批列表 |
|
||||
// */ |
|
||||
// @GetMapping("/pending/{assignee}") |
|
||||
// public Map<String, Object> getPendingApprovals(@PathVariable String assignee) { |
|
||||
// List<CashRecordDone> approvals = processService.getPendingApprovals(assignee); |
|
||||
// |
|
||||
// Map<String, Object> result = new HashMap<>(); |
|
||||
// result.put("success", true); |
|
||||
// result.put("data", approvals); |
|
||||
// result.put("count", approvals.size()); |
|
||||
// |
|
||||
// return result; |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 获取审批详情 |
|
||||
// */ |
|
||||
// @GetMapping("/detail/{orderId}") |
|
||||
// public Map<String, Object> getApprovalDetail(@PathVariable Long orderId) { |
|
||||
// Map<String, Object> detail = processService.getApprovalDetail(orderId); |
|
||||
// |
|
||||
// Map<String, Object> result = new HashMap<>(); |
|
||||
// if (detail != null) { |
|
||||
// result.put("success", true); |
|
||||
// result.put("data", detail); |
|
||||
// } else { |
|
||||
// result.put("success", false); |
|
||||
// result.put("message", "审批单不存在"); |
|
||||
// } |
|
||||
// |
|
||||
// return result; |
|
||||
// } |
|
||||
//} |
|
||||
|
package com.example.demo.controller.cash; |
||||
|
|
||||
|
|
||||
|
import com.example.demo.Util.JWTUtil; |
||||
|
import com.example.demo.domain.entity.Admin; |
||||
|
import com.example.demo.domain.vo.cash.CashRecordDone; |
||||
|
import com.example.demo.domain.vo.coin.Page; |
||||
|
import com.example.demo.domain.vo.coin.Result; |
||||
|
import com.example.demo.service.coin.MarketService; |
||||
|
import com.example.demo.serviceImpl.cash.CashRefundServiceImpl; |
||||
|
import jakarta.annotation.Resource; |
||||
|
import jakarta.servlet.http.HttpServletRequest; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.util.ObjectUtils; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
import org.springframework.web.context.request.RequestContextHolder; |
||||
|
import org.springframework.web.context.request.ServletRequestAttributes; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* @program: GOLD |
||||
|
* @ClassName RefundController |
||||
|
* @description: |
||||
|
* @author: huangqizhen |
||||
|
* @create: 2025−09-26 14:15 |
||||
|
* @Version 1.0 |
||||
|
**/ |
||||
|
@RestController |
||||
|
@RequestMapping("/Money") |
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
@CrossOrigin |
||||
|
public class CashRefundController { |
||||
|
@Autowired |
||||
|
private CashRefundServiceImpl cashRefundServiceImpl; |
||||
|
@Autowired |
||||
|
MarketService marketService; |
||||
|
|
||||
|
@PostMapping("/select") |
||||
|
public Result select(@RequestBody Page page) throws Exception { |
||||
|
// 获取当前请求对象 |
||||
|
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); |
||||
|
String token = request.getHeader("token"); |
||||
|
|
||||
|
// 解析 token 获取用户信息 |
||||
|
Admin admin = (Admin) JWTUtil.getUserDetailsList(String.valueOf(token), Admin.class); |
||||
|
List<String> userMarkets = Arrays.asList(StringUtils.split(admin.getMarkets(), ",")); |
||||
|
List<String> markets = marketService.getMarketIds(userMarkets); |
||||
|
|
||||
|
// 校验分页参数 |
||||
|
if (ObjectUtils.isEmpty(page.getPageNum())) { |
||||
|
return Result.error("页码数为空!"); |
||||
|
} |
||||
|
if (ObjectUtils.isEmpty(page.getPageSize())) { |
||||
|
return Result.error("页大小为空!"); |
||||
|
} |
||||
|
|
||||
|
// 获取传入的市场列表 |
||||
|
List<String> requestedMarkets = page.getCashRecordDone() != null ? page.getCashRecordDone().getMarkets() : null; |
||||
|
|
||||
|
// 权限校验逻辑 |
||||
|
if (markets.contains("9") || markets.contains("9999")) { |
||||
|
// 特权市场:9 或 9999,跳过权限校验,直接放行传入的 markets |
||||
|
// 如果业务需要,也可以在这里做空值处理 |
||||
|
if (page.getCashRecordDone() != null) { |
||||
|
// 保持 requestedMarkets 不变,原样接受 |
||||
|
// 可选:如果 requestedMarkets 为 null,可设为默认值或保持 null |
||||
|
} |
||||
|
} else { |
||||
|
// 普通用户:必须校验权限 |
||||
|
if (requestedMarkets == null || requestedMarkets.isEmpty()) { |
||||
|
page.getCashRecordDone().setMarkets(requestedMarkets); |
||||
|
} |
||||
|
if (!markets.containsAll(requestedMarkets)) { |
||||
|
return Result.error("无权限!请求的市场不在授权范围内。"); |
||||
|
} |
||||
|
// 校验通过,保持 requestedMarkets 不变 |
||||
|
} |
||||
|
return Result.success(cashRefundServiceImpl.select(page.getPageNum(),page.getPageSize(),page.getCashRecordDone())); |
||||
|
} |
||||
|
@PostMapping("/add") |
||||
|
public Result add(@RequestBody CashRecordDone cashCollection){ |
||||
|
return Result.success(cashRefundServiceImpl.add(cashCollection)); |
||||
|
} |
||||
|
@PostMapping("/update") |
||||
|
public Result update(@RequestBody CashRecordDone cashRecordDone){ |
||||
|
return Result.success(cashRefundServiceImpl.update(cashRecordDone)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,244 @@ |
|||||
|
package com.example.demo.domain.vo.cash; |
||||
|
|
||||
|
import com.alibaba.excel.annotation.ExcelIgnore; |
||||
|
import com.alibaba.excel.annotation.ExcelProperty; |
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
@Data |
||||
|
public class CashRecord { |
||||
|
@ExcelProperty("序号") |
||||
|
private Integer id; |
||||
|
|
||||
|
@ExcelProperty("订单类型") |
||||
|
private Integer orderType; // 1:收款,2:退款 |
||||
|
|
||||
|
@ExcelProperty("精网号") |
||||
|
private Integer jwcode; |
||||
|
|
||||
|
@ExcelProperty("姓名") |
||||
|
private String name; |
||||
|
|
||||
|
@ExcelProperty("所属地区") |
||||
|
private String market; |
||||
|
|
||||
|
@ExcelProperty("活动名称") |
||||
|
private String activity; |
||||
|
|
||||
|
@ExcelProperty("金币订单号") |
||||
|
private String orderCode; |
||||
|
|
||||
|
@ExcelProperty("银行流水订单号") |
||||
|
private String bankCode; |
||||
|
|
||||
|
@ExcelProperty("商品名称") |
||||
|
private String goodsName; |
||||
|
|
||||
|
@ExcelProperty("产品数量") |
||||
|
private Integer goodNum; |
||||
|
|
||||
|
@ExcelProperty("币种") |
||||
|
private String paymentCurrency; |
||||
|
|
||||
|
@ExcelProperty("金额") |
||||
|
private BigDecimal paymentAmount; |
||||
|
|
||||
|
@ExcelProperty("到账币种") |
||||
|
private String receivedCurrency; |
||||
|
|
||||
|
@ExcelProperty("到账金额") |
||||
|
private BigDecimal receivedAmount; |
||||
|
|
||||
|
@ExcelProperty("手续费") |
||||
|
private BigDecimal handlingCharge; |
||||
|
|
||||
|
@ExcelProperty("支付方式") |
||||
|
private String payType; |
||||
|
|
||||
|
@ExcelProperty("到账地区") |
||||
|
private String receivedMarket; |
||||
|
|
||||
|
@ExcelProperty("付款日期") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date payTime; |
||||
|
|
||||
|
@ExcelProperty("到账日期") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date receivedTime; |
||||
|
|
||||
|
@ExcelIgnore |
||||
|
private Integer auditId; |
||||
|
|
||||
|
@ExcelProperty("订单状态") |
||||
|
private Integer status; |
||||
|
|
||||
|
@ExcelIgnore |
||||
|
private Integer submitterId; |
||||
|
|
||||
|
@ExcelProperty("转账凭证") |
||||
|
private String voucher; |
||||
|
|
||||
|
@ExcelProperty("备注") |
||||
|
private String remark; |
||||
|
|
||||
|
@ExcelProperty("驳回理由") |
||||
|
private String rejectReason; |
||||
|
|
||||
|
@ExcelProperty("退款理由") |
||||
|
private String refundReason; |
||||
|
|
||||
|
@ExcelProperty("退款方式") |
||||
|
private Integer refundModel; |
||||
|
|
||||
|
@ExcelProperty("退款执行人") |
||||
|
private Integer executor; |
||||
|
|
||||
|
@ExcelProperty("退款途径") |
||||
|
private String refundChannels; |
||||
|
|
||||
|
@ExcelProperty("退款日期") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai") |
||||
|
private Date refundTime; |
||||
|
|
||||
|
@ExcelProperty("退款备注") |
||||
|
private String refundRemark; |
||||
|
|
||||
|
@ExcelProperty("退款截图") |
||||
|
private String refundVoucher; |
||||
|
|
||||
|
@ExcelProperty("创建时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@ExcelProperty("更新时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date updateTime; |
||||
|
|
||||
|
@ExcelProperty("审核时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date auditTime; |
||||
|
|
||||
|
@ExcelProperty("收款备注") |
||||
|
private String receivedRemark; |
||||
|
|
||||
|
@ExcelIgnore |
||||
|
private Integer marketId; // 关联market表的id |
||||
|
|
||||
|
|
||||
|
@ExcelIgnore |
||||
|
private Integer receivedMarketId; // 到账地区ID |
||||
|
|
||||
|
// 获取订单的主要地区ID(用于审批权限判断) |
||||
|
public Integer getPrimaryMarketId() { |
||||
|
return marketId != null ? marketId : (receivedMarketId != null ? receivedMarketId : null); |
||||
|
} |
||||
|
|
||||
|
// 获取订单的主要地区名称 |
||||
|
public String getPrimaryMarketName() { |
||||
|
return market != null ? market : (receivedMarket != null ? receivedMarket : null); |
||||
|
} |
||||
|
|
||||
|
// 退款状态常量 |
||||
|
public static final Integer STATUS_REFUND_REGIONAL_FINANCE_PENDING = 10; // 地区财务待审核 |
||||
|
public static final Integer STATUS_REFUND_WITHDRAWN = 11; // 手动撤回待编辑提交 |
||||
|
public static final Integer STATUS_REFUND_REGIONAL_FINANCE_REJECTED = 12; // 地区财务驳回 |
||||
|
public static final Integer STATUS_REFUND_REGIONAL_MANAGER_PENDING = 20; // 地区负责人待审核 |
||||
|
public static final Integer STATUS_REFUND_REGIONAL_MANAGER_REJECTED = 22; // 地区负责人驳回 |
||||
|
public static final Integer STATUS_REFUND_HEADQUARTERS_FINANCE_PENDING = 30; // 总部财务待审核 |
||||
|
public static final Integer STATUS_REFUND_HEADQUARTERS_FINANCE_REJECTED = 32; // 总部财务驳回 |
||||
|
public static final Integer STATUS_REFUND_EXECUTOR_PENDING = 40; // 执行人待处理 |
||||
|
public static final Integer STATUS_REFUND_COMPLETED = 41; // 执行人已处理,退款结束 |
||||
|
|
||||
|
// 获取当前审批级别(只处理退款订单) |
||||
|
public Integer getCurrentApprovalLevel() { |
||||
|
if (!isRefundOrder()) { |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
switch (status) { |
||||
|
case 10: return 1; // 地区财务待审核 |
||||
|
case 11: return 0; // 手动撤回待编辑提交 |
||||
|
case 12: return -1; // 地区财务驳回 |
||||
|
case 20: return 2; // 地区负责人待审核 |
||||
|
case 22: return -2; // 地区负责人驳回 |
||||
|
case 30: return 3; // 总部财务待审核 |
||||
|
case 32: return -3; // 总部财务驳回 |
||||
|
case 40: return 4; // 执行人待处理 |
||||
|
case 41: return 5; // 退款结束 |
||||
|
default: return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 检查是否为退款订单 |
||||
|
public boolean isRefundOrder() { |
||||
|
return orderType != null && orderType == 2; |
||||
|
} |
||||
|
|
||||
|
// 检查是否可以撤回(只有在地区财务待审核状态可以撤回) |
||||
|
public boolean canWithdraw() { |
||||
|
return isRefundOrder() && status != null && status == STATUS_REFUND_REGIONAL_FINANCE_PENDING; |
||||
|
} |
||||
|
|
||||
|
// 检查是否可以重新提交(只有在撤回状态可以重新提交) |
||||
|
public boolean canResubmit() { |
||||
|
return isRefundOrder() && status != null && status == STATUS_REFUND_WITHDRAWN; |
||||
|
} |
||||
|
|
||||
|
// 检查是否在审批流程中 |
||||
|
public boolean isInApprovalProcess() { |
||||
|
return isRefundOrder() && status != null && |
||||
|
(status == STATUS_REFUND_REGIONAL_FINANCE_PENDING || |
||||
|
status == STATUS_REFUND_REGIONAL_MANAGER_PENDING || |
||||
|
status == STATUS_REFUND_HEADQUARTERS_FINANCE_PENDING || |
||||
|
status == STATUS_REFUND_EXECUTOR_PENDING); |
||||
|
} |
||||
|
|
||||
|
// 获取下一个状态 |
||||
|
public Integer getNextStatus(boolean approved) { |
||||
|
if (!isRefundOrder()) { |
||||
|
return status; |
||||
|
} |
||||
|
|
||||
|
if (!approved) { |
||||
|
// 驳回状态 |
||||
|
switch (status) { |
||||
|
case 10: return STATUS_REFUND_REGIONAL_FINANCE_REJECTED; |
||||
|
case 20: return STATUS_REFUND_REGIONAL_MANAGER_REJECTED; |
||||
|
case 30: return STATUS_REFUND_HEADQUARTERS_FINANCE_REJECTED; |
||||
|
default: return status; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 审批通过状态 |
||||
|
switch (status) { |
||||
|
case 10: return STATUS_REFUND_REGIONAL_MANAGER_PENDING; |
||||
|
case 20: return STATUS_REFUND_HEADQUARTERS_FINANCE_PENDING; |
||||
|
case 30: return STATUS_REFUND_EXECUTOR_PENDING; |
||||
|
case 40: return STATUS_REFUND_COMPLETED; |
||||
|
default: return status; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 撤回订单 |
||||
|
public boolean withdraw() { |
||||
|
if (!canWithdraw()) { |
||||
|
return false; |
||||
|
} |
||||
|
this.status = STATUS_REFUND_WITHDRAWN; |
||||
|
this.updateTime = new Date(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 重新提交订单 |
||||
|
public boolean resubmit() { |
||||
|
if (!canResubmit()) { |
||||
|
return false; |
||||
|
} |
||||
|
this.status = STATUS_REFUND_REGIONAL_FINANCE_PENDING; |
||||
|
this.updateTime = new Date(); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
package com.example.demo.domain.vo.cash; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
@Data |
||||
|
public class Market { |
||||
|
private Integer id; |
||||
|
private String name; // 地区名称 |
||||
|
private Integer parentId; // 父级ID |
||||
|
private Date createTime; // 创建时间 |
||||
|
private String treelist; // 树形路径 |
||||
|
private Integer type; // 等级类型 |
||||
|
|
||||
|
// 获取地区层级 |
||||
|
public Integer getLevel() { |
||||
|
if (treelist == null) return 0; |
||||
|
String[] levels = treelist.split("-"); |
||||
|
return levels.length - 1; // 减去根节点 |
||||
|
} |
||||
|
|
||||
|
// 判断是否为根节点 |
||||
|
public boolean isRoot() { |
||||
|
return parentId == -1; |
||||
|
} |
||||
|
|
||||
|
// 判断是否为市场部节点 |
||||
|
public boolean isMarketDepartment() { |
||||
|
return parentId != null && parentId == 3; // 市场部的父ID是3 |
||||
|
} |
||||
|
|
||||
|
// 判断是否为具体市场(叶子节点) |
||||
|
public boolean isSpecificMarket() { |
||||
|
return type != null && type == 2; // 等级类型为2的是具体市场 |
||||
|
} |
||||
|
|
||||
|
// 获取根节点名称 |
||||
|
public String getRootName() { |
||||
|
if (treelist == null) return null; |
||||
|
String[] levels = treelist.split("-"); |
||||
|
return levels.length > 0 ? levels[0] : null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
package com.example.demo.domain.vo.cash; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
import lombok.Data; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
@Data |
||||
|
public class RefundApprovalRecord { |
||||
|
private Integer id; |
||||
|
private Integer cashRecordId; |
||||
|
private Integer approvalLevel; |
||||
|
private String approvalNode; |
||||
|
private Integer approverId; |
||||
|
private String approverName; |
||||
|
private String approvalResult; // APPROVED, REJECTED |
||||
|
private String comments; |
||||
|
|
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date approvalTime; |
||||
|
|
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date createTime; |
||||
|
|
||||
|
// 撤回记录相关字段 |
||||
|
private Boolean isWithdrawal = false; |
||||
|
private Integer withdrawnBy; |
||||
|
private String withdrawReason; |
||||
|
|
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") |
||||
|
private Date withdrawTime; |
||||
|
|
||||
|
public RefundApprovalRecord() {} |
||||
|
|
||||
|
public RefundApprovalRecord(Integer cashRecordId, Integer approvalLevel, String approvalNode, |
||||
|
Integer approverId, String approverName) { |
||||
|
this.cashRecordId = cashRecordId; |
||||
|
this.approvalLevel = approvalLevel; |
||||
|
this.approvalNode = approvalNode; |
||||
|
this.approverId = approverId; |
||||
|
this.approverName = approverName; |
||||
|
this.createTime = new Date(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
package com.example.demo.domain.vo.cash; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import java.util.HashSet; |
||||
|
import java.util.Set; |
||||
|
|
||||
|
@Data |
||||
|
public class User { |
||||
|
private Integer id; |
||||
|
private String name; |
||||
|
private Integer marketId; // 关联market表的id |
||||
|
private String marketName; // 地区名称 |
||||
|
private Set<String> roles = new HashSet<>(); |
||||
|
private Set<Integer> managedMarketIds = new HashSet<>(); // 管辖的地区ID列表 |
||||
|
private Set<String> managedMarketNames = new HashSet<>(); // 管辖的地区名称列表 |
||||
|
|
||||
|
// 角色权限检查 |
||||
|
public boolean hasRole(String role) { |
||||
|
return roles != null && roles.contains(role); |
||||
|
} |
||||
|
|
||||
|
public boolean hasAnyRole(Set<String> requiredRoles) { |
||||
|
if (roles == null || requiredRoles == null) return false; |
||||
|
return requiredRoles.stream().anyMatch(roles::contains); |
||||
|
} |
||||
|
|
||||
|
// 地区权限检查 - 基于地区ID |
||||
|
public boolean canApproveMarket(Integer targetMarketId) { |
||||
|
if (targetMarketId == null) return false; |
||||
|
|
||||
|
// 优先检查管辖地区ID列表 |
||||
|
if (managedMarketIds != null && !managedMarketIds.isEmpty()) { |
||||
|
return managedMarketIds.contains(targetMarketId); |
||||
|
} |
||||
|
|
||||
|
// 如果没有管辖地区列表,使用单个marketId字段 |
||||
|
return targetMarketId.equals(this.marketId); |
||||
|
} |
||||
|
|
||||
|
public boolean canApproveAnyMarket(Set<Integer> targetMarketIds) { |
||||
|
if (targetMarketIds == null || targetMarketIds.isEmpty()) return false; |
||||
|
|
||||
|
if (managedMarketIds != null && !managedMarketIds.isEmpty()) { |
||||
|
return targetMarketIds.stream().anyMatch(managedMarketIds::contains); |
||||
|
} |
||||
|
|
||||
|
return targetMarketIds.contains(this.marketId); |
||||
|
} |
||||
|
|
||||
|
// 地区权限检查 - 基于地区名称(向后兼容) |
||||
|
public boolean canApproveMarketByName(String targetMarketName) { |
||||
|
if (targetMarketName == null) return false; |
||||
|
|
||||
|
if (managedMarketNames != null && !managedMarketNames.isEmpty()) { |
||||
|
return managedMarketNames.contains(targetMarketName); |
||||
|
} |
||||
|
|
||||
|
return targetMarketName.equals(this.marketName); |
||||
|
} |
||||
|
|
||||
|
// 获取用户的所有管辖地区ID |
||||
|
public Set<Integer> getAllManagedMarketIds() { |
||||
|
Set<Integer> allMarketIds = new HashSet<>(); |
||||
|
if (managedMarketIds != null && !managedMarketIds.isEmpty()) { |
||||
|
allMarketIds.addAll(managedMarketIds); |
||||
|
} |
||||
|
if (marketId != null) { |
||||
|
allMarketIds.add(marketId); |
||||
|
} |
||||
|
return allMarketIds; |
||||
|
} |
||||
|
|
||||
|
// 获取用户的所有管辖地区名称 |
||||
|
public Set<String> getAllManagedMarketNames() { |
||||
|
Set<String> allMarketNames = new HashSet<>(); |
||||
|
if (managedMarketNames != null && !managedMarketNames.isEmpty()) { |
||||
|
allMarketNames.addAll(managedMarketNames); |
||||
|
} |
||||
|
if (marketName != null) { |
||||
|
allMarketNames.add(marketName); |
||||
|
} |
||||
|
return allMarketNames; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,173 @@ |
|||||
|
package com.example.demo.service.cash; |
||||
|
|
||||
|
|
||||
|
import com.example.demo.domain.vo.cash.Market; |
||||
|
import com.example.demo.domain.vo.cash.User; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.*; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Service |
||||
|
public class MarketService { |
||||
|
|
||||
|
// 地区数据缓存 |
||||
|
private final Map<Integer, Market> marketCache = new ConcurrentHashMap<>(); |
||||
|
private final Map<String, Integer> marketNameToIdCache = new ConcurrentHashMap<>(); |
||||
|
private final Map<Integer, List<Market>> childrenCache = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
// 初始化地区数据(实际项目中应从数据库加载) |
||||
|
public void initializeMarkets(List<Market> markets) { |
||||
|
marketCache.clear(); |
||||
|
marketNameToIdCache.clear(); |
||||
|
childrenCache.clear(); |
||||
|
|
||||
|
for (Market market : markets) { |
||||
|
marketCache.put(market.getId(), market); |
||||
|
marketNameToIdCache.put(market.getName(), market.getId()); |
||||
|
} |
||||
|
|
||||
|
// 构建父子关系 |
||||
|
for (Market market : markets) { |
||||
|
Integer parentId = market.getParentId(); |
||||
|
if (!childrenCache.containsKey(parentId)) { |
||||
|
childrenCache.put(parentId, new ArrayList<>()); |
||||
|
} |
||||
|
childrenCache.get(parentId).add(market); |
||||
|
} |
||||
|
|
||||
|
log.info("地区数据初始化完成,共加载{}个地区", markets.size()); |
||||
|
} |
||||
|
|
||||
|
// 根据ID获取地区 |
||||
|
public Market getMarketById(Integer id) { |
||||
|
return marketCache.get(id); |
||||
|
} |
||||
|
|
||||
|
// 根据名称获取地区 |
||||
|
public Market getMarketByName(String name) { |
||||
|
Integer id = marketNameToIdCache.get(name); |
||||
|
return id != null ? marketCache.get(id) : null; |
||||
|
} |
||||
|
|
||||
|
// 根据名称获取地区ID |
||||
|
public Integer getMarketIdByName(String name) { |
||||
|
return marketNameToIdCache.get(name); |
||||
|
} |
||||
|
|
||||
|
// 获取所有子地区(包括所有后代) |
||||
|
public List<Market> getAllChildren(Integer parentId) { |
||||
|
List<Market> allChildren = new ArrayList<>(); |
||||
|
List<Market> directChildren = childrenCache.get(parentId); |
||||
|
|
||||
|
if (directChildren != null) { |
||||
|
allChildren.addAll(directChildren); |
||||
|
for (Market child : directChildren) { |
||||
|
allChildren.addAll(getAllChildren(child.getId())); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return allChildren; |
||||
|
} |
||||
|
|
||||
|
// 获取所有子地区ID(包括所有后代) |
||||
|
public Set<Integer> getAllChildrenIds(Integer parentId) { |
||||
|
return getAllChildren(parentId).stream() |
||||
|
.map(Market::getId) |
||||
|
.collect(Collectors.toSet()); |
||||
|
} |
||||
|
|
||||
|
// 获取所有子地区名称(包括所有后代) |
||||
|
public Set<String> getAllChildrenNames(Integer parentId) { |
||||
|
return getAllChildren(parentId).stream() |
||||
|
.map(Market::getName) |
||||
|
.collect(Collectors.toSet()); |
||||
|
} |
||||
|
|
||||
|
// 获取直接子地区 |
||||
|
public List<Market> getDirectChildren(Integer parentId) { |
||||
|
return childrenCache.getOrDefault(parentId, new ArrayList<>()); |
||||
|
} |
||||
|
|
||||
|
// 获取市场部下的所有具体市场 |
||||
|
public List<Market> getSpecificMarketsUnderMarketDepartment() { |
||||
|
List<Market> specificMarkets = new ArrayList<>(); |
||||
|
|
||||
|
// 市场部的ID是3 |
||||
|
List<Market> marketDepartmentChildren = getDirectChildren(3); |
||||
|
for (Market market : marketDepartmentChildren) { |
||||
|
if (market.isSpecificMarket()) { |
||||
|
specificMarkets.add(market); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return specificMarkets; |
||||
|
} |
||||
|
|
||||
|
// 获取市场部下的所有具体市场ID |
||||
|
public Set<Integer> getSpecificMarketIdsUnderMarketDepartment() { |
||||
|
return getSpecificMarketsUnderMarketDepartment().stream() |
||||
|
.map(Market::getId) |
||||
|
.collect(Collectors.toSet()); |
||||
|
} |
||||
|
|
||||
|
// 获取用户管辖的所有具体市场ID(包括子地区) |
||||
|
public Set<Integer> getUserManagedSpecificMarketIds(User user) { |
||||
|
Set<Integer> allSpecificMarketIds = new HashSet<>(); |
||||
|
|
||||
|
Set<Integer> userMarketIds = user.getAllManagedMarketIds(); |
||||
|
for (Integer marketId : userMarketIds) { |
||||
|
Market market = getMarketById(marketId); |
||||
|
if (market != null && market.isSpecificMarket()) { |
||||
|
allSpecificMarketIds.add(marketId); |
||||
|
} else { |
||||
|
// 如果不是具体市场,获取其下的所有具体市场 |
||||
|
allSpecificMarketIds.addAll(getAllChildrenIds(marketId).stream() |
||||
|
.filter(id -> { |
||||
|
Market child = getMarketById(id); |
||||
|
return child != null && child.isSpecificMarket(); |
||||
|
}) |
||||
|
.collect(Collectors.toSet())); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return allSpecificMarketIds; |
||||
|
} |
||||
|
|
||||
|
// 验证地区是否存在 |
||||
|
public boolean marketExists(Integer marketId) { |
||||
|
return marketCache.containsKey(marketId); |
||||
|
} |
||||
|
|
||||
|
// 验证地区名称是否存在 |
||||
|
public boolean marketNameExists(String marketName) { |
||||
|
return marketNameToIdCache.containsKey(marketName); |
||||
|
} |
||||
|
|
||||
|
// 获取所有地区 |
||||
|
public List<Market> getAllMarkets() { |
||||
|
return new ArrayList<>(marketCache.values()); |
||||
|
} |
||||
|
|
||||
|
// 获取所有具体市场(叶子节点) |
||||
|
public List<Market> getAllSpecificMarkets() { |
||||
|
return marketCache.values().stream() |
||||
|
.filter(Market::isSpecificMarket) |
||||
|
.collect(Collectors.toList()); |
||||
|
} |
||||
|
|
||||
|
// 根据地区ID获取完整路径 |
||||
|
public String getMarketFullPath(Integer marketId) { |
||||
|
Market market = marketCache.get(marketId); |
||||
|
return market != null ? market.getTreelist() : null; |
||||
|
} |
||||
|
|
||||
|
// 根据地区名称获取完整路径 |
||||
|
public String getMarketFullPathByName(String marketName) { |
||||
|
Integer marketId = marketNameToIdCache.get(marketName); |
||||
|
return marketId != null ? getMarketFullPath(marketId) : null; |
||||
|
} |
||||
|
} |
||||
@ -1,221 +0,0 @@ |
|||||
//package com.example.demo.service.cash; |
|
||||
// |
|
||||
// |
|
||||
//import com.example.demo.domain.vo.cash.CashRecordDone; |
|
||||
//import com.example.demo.mapper.cash.CashRefundMapper; |
|
||||
//import org.flowable.engine.*; |
|
||||
//import org.flowable.engine.runtime.ProcessInstance; |
|
||||
//import org.flowable.task.api.Task; |
|
||||
//import org.springframework.beans.factory.annotation.Autowired; |
|
||||
//import org.springframework.stereotype.Service; |
|
||||
//import org.springframework.transaction.annotation.Transactional; |
|
||||
// |
|
||||
//import java.util.HashMap; |
|
||||
//import java.util.List; |
|
||||
//import java.util.Map; |
|
||||
//import java.util.UUID; |
|
||||
// |
|
||||
//@Service |
|
||||
//@Transactional |
|
||||
//public class ProcessService { |
|
||||
// |
|
||||
// @Autowired |
|
||||
// private RuntimeService runtimeService; |
|
||||
// |
|
||||
// @Autowired |
|
||||
// private TaskService taskService; |
|
||||
// |
|
||||
// @Autowired |
|
||||
// private RepositoryService repositoryService; |
|
||||
// |
|
||||
// @Autowired |
|
||||
// private HistoryService historyService; |
|
||||
// |
|
||||
// @Autowired |
|
||||
// private CashRefundMapper cashRefundMapper; // 新增 |
|
||||
// |
|
||||
// /** |
|
||||
// * 提交审批申请 |
|
||||
// */ |
|
||||
// public Map<String, Object> submitApproval(CashRecordDone order) { |
|
||||
// // 生成订单号 |
|
||||
// String orderNumber = UUID.randomUUID().toString().replaceAll("-", ""); |
|
||||
// order.setOrderCode("XJTK" + orderNumber); |
|
||||
// order.setStatus(10); // 待提交 |
|
||||
// order.setOrderType(2); |
|
||||
// |
|
||||
// // 保存业务数据 |
|
||||
// cashRefundMapper.insert(order); |
|
||||
// |
|
||||
// // 准备流程变量 |
|
||||
// Map<String, Object> variables = new HashMap<>(); |
|
||||
// variables.put("name", order.getName()); |
|
||||
// variables.put("payment_currency", order.getPaymentCurrency()); |
|
||||
// variables.put("payment_amount", order.getPaymentAmount()); |
|
||||
// variables.put("goods_name", order.getGoodsName()); |
|
||||
// variables.put("market", order.getMarket()); |
|
||||
// variables.put("activity", order.getActivity()); |
|
||||
// variables.put("goods_num", order.getGoodsNum()); |
|
||||
// variables.put("pay_type", order.getPayType()); |
|
||||
//// variables.put("received_market", order.getReceivedMarket()); |
|
||||
// variables.put("market_list", order.getMarkets()); |
|
||||
// variables.put("jwcode", order.getJwcode()); |
|
||||
// variables.put("voucher", order.getVoucher()); |
|
||||
//// variables.put("bank_code", order.getBankCode()); |
|
||||
// variables.put("refund_channels", order.getRefundChannels()); |
|
||||
// variables.put("refund_model", order.getRefundModel()); |
|
||||
// variables.put("refund_reason", order.getRefundReason()); |
|
||||
// variables.put("remark", order.getRemark()); |
|
||||
// variables.put("executor", order.getExecutor()); |
|
||||
// variables.put("id", order.getId()); |
|
||||
// |
|
||||
// // 启动流程 |
|
||||
// ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("work", variables); |
|
||||
// |
|
||||
// // 更新业务数据的流程信息 |
|
||||
// order.setProcessInstanceId(processInstance.getId()); |
|
||||
// |
|
||||
// // 获取第一个任务并更新当前处理人 |
|
||||
// Task currentTask = taskService.createTaskQuery() |
|
||||
// .processInstanceId(processInstance.getId()) |
|
||||
// .singleResult(); |
|
||||
// if (currentTask != null) { |
|
||||
// order.setCurrentTaskId(currentTask.getId()); |
|
||||
// order.setAuditId(currentTask.getAssignee()); |
|
||||
// order.setStatus(20); // 当地财务审核中 |
|
||||
// } |
|
||||
// |
|
||||
// cashRefundMapper.update(order); |
|
||||
// |
|
||||
// Map<String, Object> result = new HashMap<>(); |
|
||||
// result.put("success", true); |
|
||||
// result.put("orderId", order.getId()); |
|
||||
// result.put("processInstanceId", processInstance.getId()); |
|
||||
// result.put("taskId", currentTask != null ? currentTask.getId() : null); |
|
||||
// |
|
||||
// return result; |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 当地财务审核 |
|
||||
// */ |
|
||||
// public void localFinanceApprove(String taskId, boolean approved, String comment) { |
|
||||
// Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); |
|
||||
// String processInstanceId = task.getProcessInstanceId(); |
|
||||
// |
|
||||
// // 更新业务数据 |
|
||||
// CashRecordDone order = cashRefundMapper.select(processInstanceId); |
|
||||
// if (order != null) { |
|
||||
// cashRefundMapper.update(order.getId(), comment); |
|
||||
// } |
|
||||
// |
|
||||
// String status = approved ? "20" : "12"; // 20=通过, 12=驳回 |
|
||||
// completeTask(taskId, status, comment, order); |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 地区负责人审核 |
|
||||
// */ |
|
||||
// public void regionalManagerApprove(String taskId, boolean approved, String comment) { |
|
||||
// Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); |
|
||||
// String processInstanceId = task.getProcessInstanceId(); |
|
||||
// |
|
||||
// // 更新业务数据 |
|
||||
// CashRecordDone order = cashRefundMapper.select(processInstanceId); |
|
||||
// if (order != null) { |
|
||||
// cashRefundMapper.update(order.getId(), comment); |
|
||||
// } |
|
||||
// |
|
||||
// String status = approved ? "30" : "22"; // 30=通过, 22=驳回 |
|
||||
// completeTask(taskId, status, comment, order); |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 总部财务审批 |
|
||||
// */ |
|
||||
// public void headFinanceApprove(String taskId, boolean approved, String comment) { |
|
||||
// Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); |
|
||||
// String processInstanceId = task.getProcessInstanceId(); |
|
||||
// |
|
||||
// // 更新业务数据 |
|
||||
// CashRecordDone order = cashRefundMapper.select(processInstanceId); |
|
||||
// if (order != null) { |
|
||||
// cashRefundMapper.update(order.getId(), comment); |
|
||||
// } |
|
||||
// |
|
||||
// String status = approved ? "40" : "32"; // 40=通过, 32=驳回 |
|
||||
// completeTask(taskId, status, comment, order); |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 完成任务并更新业务数据 |
|
||||
// */ |
|
||||
// private void completeTask(String taskId, String status, String comment, CashRecordDone order) { |
|
||||
// Map<String, Object> variables = new HashMap<>(); |
|
||||
// variables.put("status", status); |
|
||||
// |
|
||||
// // 添加审批意见 |
|
||||
// if (comment != null && !comment.trim().isEmpty()) { |
|
||||
// taskService.addComment(taskId, null, comment); |
|
||||
// } |
|
||||
// |
|
||||
// taskService.complete(taskId, variables); |
|
||||
// |
|
||||
// // 更新业务数据状态 |
|
||||
// if (order != null) { |
|
||||
// cashRefundMapper.update(order.getId(), status); |
|
||||
// |
|
||||
// // 如果不是结束状态,更新当前任务信息 |
|
||||
// if (!status.equals("12") && !status.equals("22") && !status.equals("32") && !status.equals("40")) { |
|
||||
// Task nextTask = taskService.createTaskQuery() |
|
||||
// .processInstanceId(order.getProcessInstanceId()) |
|
||||
// .singleResult(); |
|
||||
// if (nextTask != null) { |
|
||||
// order.setCurrentTaskId(nextTask.getId()); |
|
||||
// order.setCurrentAssignee(nextTask.getAssignee()); |
|
||||
// cashRefundMapper.update(order); |
|
||||
// } |
|
||||
// } |
|
||||
// } |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 根据用户获取待办审批 |
|
||||
// */ |
|
||||
// public List<CashRecordDone> getPendingApprovals(String assignee) { |
|
||||
// return cashRefundMapper.select(assignee); |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 根据申请人获取历史审批 |
|
||||
// */ |
|
||||
// public List<CashRecordDone> getApprovalHistory(String applicant) { |
|
||||
// return cashRefundMapper.select(applicant); |
|
||||
// } |
|
||||
// |
|
||||
// /** |
|
||||
// * 获取审批详情 |
|
||||
// */ |
|
||||
// public Map<String, Object> getApprovalDetail(Long orderId) { |
|
||||
// CashRecordDone order = cashRefundMapper.select(orderId); |
|
||||
// if (order == null) { |
|
||||
// return null; |
|
||||
// } |
|
||||
// |
|
||||
// Map<String, Object> result = new HashMap<>(); |
|
||||
// result.put("order", order); |
|
||||
// |
|
||||
// // 获取流程历史 |
|
||||
// if (order.getProcessInstanceId() != null) { |
|
||||
// Object history = historyService.createHistoricActivityInstanceQuery() |
|
||||
// .processInstanceId(order.getProcessInstanceId()) |
|
||||
// .orderByHistoricActivityInstanceStartTime().asc() |
|
||||
// .list(); |
|
||||
// result.put("processHistory", history); |
|
||||
// } |
|
||||
// |
|
||||
// return result; |
|
||||
// } |
|
||||
// |
|
||||
// // 其他方法保持不变... |
|
||||
//} |
|
||||
@ -0,0 +1,286 @@ |
|||||
|
package com.example.demo.service.cash; |
||||
|
|
||||
|
|
||||
|
import com.example.demo.Node.RefundApprovalNode; |
||||
|
import com.example.demo.config.cash.RefundApprovalFlowConfig; |
||||
|
import com.example.demo.domain.vo.cash.CashRecord; |
||||
|
import com.example.demo.domain.vo.cash.Market; |
||||
|
import com.example.demo.domain.vo.cash.RefundApprovalRecord; |
||||
|
import com.example.demo.domain.vo.cash.User; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.*; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Service |
||||
|
public class RefundApprovalService { |
||||
|
|
||||
|
private final MarketService marketService; |
||||
|
|
||||
|
// 退款审批流程配置缓存 |
||||
|
private static final Map<String, RefundApprovalFlowConfig> REFUND_APPROVAL_FLOW_CONFIG = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
// 角色定义 |
||||
|
public static final String ROLE_REGIONAL_FINANCE = "REGIONAL_FINANCE"; |
||||
|
public static final String ROLE_REGIONAL_MANAGER = "REGIONAL_MANAGER"; |
||||
|
public static final String ROLE_HEADQUARTERS_FINANCE = "HEADQUARTERS_FINANCE"; |
||||
|
public static final String ROLE_EXECUTOR = "EXECUTOR"; |
||||
|
public static final String ROLE_SUBMITTER = "SUBMITTER"; |
||||
|
|
||||
|
public RefundApprovalService(MarketService marketService) { |
||||
|
this.marketService = marketService; |
||||
|
initializeRefundFlowConfigs(); |
||||
|
} |
||||
|
|
||||
|
// 初始化退款审批流程配置(基于地区ID) |
||||
|
private void initializeRefundFlowConfigs() { |
||||
|
// 获取市场部下的所有具体市场ID |
||||
|
Set<Integer> specificMarketIds = marketService.getSpecificMarketIdsUnderMarketDepartment(); |
||||
|
|
||||
|
// 为每个具体市场创建审批流程 |
||||
|
for (Integer marketId : specificMarketIds) { |
||||
|
Market market = marketService.getMarketById(marketId); |
||||
|
if (market != null) { |
||||
|
List<RefundApprovalNode> flowNodes = createRefundFlowForMarket(market); |
||||
|
REFUND_APPROVAL_FLOW_CONFIG.put(marketId.toString(), |
||||
|
new RefundApprovalFlowConfig(market.getName() + "退款审批流程", flowNodes)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 创建通用审批流程(用于没有特定配置的市场) |
||||
|
List<RefundApprovalNode> commonRefundFlow = createCommonRefundFlow(); |
||||
|
REFUND_APPROVAL_FLOW_CONFIG.put("COMMON", new RefundApprovalFlowConfig("通用退款审批流程", commonRefundFlow)); |
||||
|
|
||||
|
log.info("退款审批流程配置初始化完成,共配置{}个流程", REFUND_APPROVAL_FLOW_CONFIG.size()); |
||||
|
} |
||||
|
|
||||
|
private List<RefundApprovalNode> createRefundFlowForMarket(Market market) { |
||||
|
Set<Integer> marketAndChildrenIds = new HashSet<>(); |
||||
|
marketAndChildrenIds.add(market.getId()); |
||||
|
marketAndChildrenIds.addAll(marketService.getAllChildrenIds(market.getId())); |
||||
|
|
||||
|
List<RefundApprovalNode> nodes = new ArrayList<>(); |
||||
|
nodes.add(new RefundApprovalNode("地区财务审核", 1, new String[]{ROLE_REGIONAL_FINANCE}, marketAndChildrenIds, true)); |
||||
|
nodes.add(new RefundApprovalNode("地区负责人审核", 2, new String[]{ROLE_REGIONAL_MANAGER}, marketAndChildrenIds, true)); |
||||
|
nodes.add(new RefundApprovalNode("总部财务审核", 3, new String[]{ROLE_HEADQUARTERS_FINANCE}, null, true)); // 总部不限制地区 |
||||
|
nodes.add(new RefundApprovalNode("执行人处理", 4, new String[]{ROLE_EXECUTOR}, null, true)); // 执行人不限制地区 |
||||
|
|
||||
|
return nodes; |
||||
|
} |
||||
|
|
||||
|
private List<RefundApprovalNode> createCommonRefundFlow() { |
||||
|
List<RefundApprovalNode> nodes = new ArrayList<>(); |
||||
|
nodes.add(new RefundApprovalNode("地区财务审核", 1, new String[]{ROLE_REGIONAL_FINANCE}, null, true)); |
||||
|
nodes.add(new RefundApprovalNode("地区负责人审核", 2, new String[]{ROLE_REGIONAL_MANAGER}, null, true)); |
||||
|
nodes.add(new RefundApprovalNode("总部财务审核", 3, new String[]{ROLE_HEADQUARTERS_FINANCE}, null, true)); |
||||
|
nodes.add(new RefundApprovalNode("执行人处理", 4, new String[]{ROLE_EXECUTOR}, null, true)); |
||||
|
|
||||
|
return nodes; |
||||
|
} |
||||
|
|
||||
|
// 获取适合订单的审批流程(基于地区ID) |
||||
|
private RefundApprovalFlowConfig getSuitableFlowConfig(CashRecord cashRecord) { |
||||
|
Integer marketId = cashRecord.getPrimaryMarketId(); |
||||
|
if (marketId != null) { |
||||
|
// 优先根据具体地区ID获取流程 |
||||
|
RefundApprovalFlowConfig specificConfig = REFUND_APPROVAL_FLOW_CONFIG.get(marketId.toString()); |
||||
|
if (specificConfig != null) { |
||||
|
return specificConfig; |
||||
|
} |
||||
|
|
||||
|
// 如果该市场没有特定配置,查找父级市场的配置 |
||||
|
Market market = marketService.getMarketById(marketId); |
||||
|
if (market != null && market.getParentId() != null) { |
||||
|
RefundApprovalFlowConfig parentConfig = REFUND_APPROVAL_FLOW_CONFIG.get(market.getParentId().toString()); |
||||
|
if (parentConfig != null) { |
||||
|
return parentConfig; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 默认返回通用流程 |
||||
|
return REFUND_APPROVAL_FLOW_CONFIG.get("COMMON"); |
||||
|
} |
||||
|
|
||||
|
// 获取用户有权限审批的订单状态(基于地区ID) |
||||
|
public List<Integer> getUserVisibleStatuses(User user, CashRecord cashRecord) { |
||||
|
if (!cashRecord.isRefundOrder()) { |
||||
|
return new ArrayList<>(); |
||||
|
} |
||||
|
|
||||
|
RefundApprovalFlowConfig flowConfig = getSuitableFlowConfig(cashRecord); |
||||
|
if (flowConfig != null) { |
||||
|
return flowConfig.getVisibleStatuses(user, cashRecord.getPrimaryMarketId()); |
||||
|
} |
||||
|
|
||||
|
log.warn("未找到适合订单地区{}的退款审批流程配置", cashRecord.getPrimaryMarketId()); |
||||
|
return new ArrayList<>(); |
||||
|
} |
||||
|
|
||||
|
// 检查用户是否有权限审批指定订单(基于地区ID) |
||||
|
public boolean canUserApproveOrder(User user, CashRecord cashRecord) { |
||||
|
if (!cashRecord.isRefundOrder() || !cashRecord.isInApprovalProcess()) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
RefundApprovalFlowConfig flowConfig = getSuitableFlowConfig(cashRecord); |
||||
|
if (flowConfig != null) { |
||||
|
return flowConfig.canUserApproveNode(user, cashRecord.getCurrentApprovalLevel(), cashRecord.getPrimaryMarketId()); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 检查用户是否可以撤回订单(只有提交人可以在第一个审批节点前撤回) |
||||
|
public boolean canUserWithdrawOrder(User user, CashRecord cashRecord) { |
||||
|
if (!cashRecord.isRefundOrder()) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 只有订单提交人才能撤回 |
||||
|
boolean isSubmitter = cashRecord.getSubmitterId() != null && |
||||
|
cashRecord.getSubmitterId().equals(user.getId()); |
||||
|
|
||||
|
return isSubmitter && cashRecord.canWithdraw(); |
||||
|
} |
||||
|
|
||||
|
// 获取用户可审批的退款订单列表(基于地区ID) |
||||
|
public List<CashRecord> getApprovableRefundOrders(User user, List<CashRecord> allOrders) { |
||||
|
List<CashRecord> approvableOrders = new ArrayList<>(); |
||||
|
|
||||
|
// 获取用户管辖的所有具体市场ID |
||||
|
Set<Integer> userManagedMarketIds = marketService.getUserManagedSpecificMarketIds(user); |
||||
|
|
||||
|
for (CashRecord order : allOrders) { |
||||
|
if (order.isRefundOrder() && canUserApproveOrder(user, order)) { |
||||
|
// 额外检查地区权限 |
||||
|
Integer orderMarketId = order.getPrimaryMarketId(); |
||||
|
if (orderMarketId != null && userManagedMarketIds.contains(orderMarketId)) { |
||||
|
approvableOrders.add(order); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return approvableOrders; |
||||
|
} |
||||
|
|
||||
|
// 提交退款审批 |
||||
|
public boolean submitRefundApproval(User user, CashRecord cashRecord, String approvalResult, String comments) { |
||||
|
if (!canUserApproveOrder(user, cashRecord)) { |
||||
|
log.warn("用户{}没有权限审批退款订单{}", user.getId(), cashRecord.getId()); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 创建审批记录 |
||||
|
RefundApprovalRecord record = new RefundApprovalRecord(); |
||||
|
record.setCashRecordId(cashRecord.getId()); |
||||
|
record.setApprovalLevel(cashRecord.getCurrentApprovalLevel()); |
||||
|
record.setApproverId(user.getId()); |
||||
|
record.setApproverName(user.getName()); |
||||
|
record.setApprovalResult(approvalResult); |
||||
|
record.setComments(comments); |
||||
|
record.setApprovalTime(new Date()); |
||||
|
|
||||
|
// 更新订单状态 |
||||
|
boolean approved = "APPROVED".equals(approvalResult); |
||||
|
Integer nextStatus = cashRecord.getNextStatus(approved); |
||||
|
cashRecord.setStatus(nextStatus); |
||||
|
cashRecord.setUpdateTime(new Date()); |
||||
|
cashRecord.setAuditId(user.getId()); |
||||
|
cashRecord.setAuditTime(new Date()); |
||||
|
|
||||
|
if (!approved) { |
||||
|
cashRecord.setRejectReason(comments); |
||||
|
} |
||||
|
|
||||
|
log.info("用户{}审批退款订单{},结果:{},新状态:{}", |
||||
|
user.getName(), cashRecord.getId(), approvalResult, nextStatus); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 撤回退款订单(在第一个审批节点前) |
||||
|
public boolean withdrawRefundOrder(User user, CashRecord cashRecord, String withdrawReason) { |
||||
|
if (!canUserWithdrawOrder(user, cashRecord)) { |
||||
|
log.warn("用户{}不能撤回退款订单{}", user.getId(), cashRecord.getId()); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 创建撤回记录 |
||||
|
RefundApprovalRecord record = new RefundApprovalRecord(); |
||||
|
record.setCashRecordId(cashRecord.getId()); |
||||
|
record.setApprovalLevel(cashRecord.getCurrentApprovalLevel()); |
||||
|
record.setApproverId(user.getId()); |
||||
|
record.setApproverName(user.getName()); |
||||
|
record.setIsWithdrawal(true); |
||||
|
record.setWithdrawnBy(user.getId()); |
||||
|
record.setWithdrawReason(withdrawReason); |
||||
|
record.setWithdrawTime(new Date()); |
||||
|
|
||||
|
// 更新订单状态为撤回 |
||||
|
boolean success = cashRecord.withdraw(); |
||||
|
if (success) { |
||||
|
cashRecord.setUpdateTime(new Date()); |
||||
|
log.info("用户{}撤回退款订单{},原因:{}", user.getName(), cashRecord.getId(), withdrawReason); |
||||
|
} |
||||
|
|
||||
|
return success; |
||||
|
} |
||||
|
|
||||
|
// 重新提交撤回的退款订单 |
||||
|
public boolean resubmitRefundOrder(User user, CashRecord cashRecord) { |
||||
|
if (!cashRecord.canResubmit()) { |
||||
|
log.warn("退款订单{}不能重新提交", cashRecord.getId()); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 检查用户是否是原提交人 |
||||
|
boolean isSubmitter = cashRecord.getSubmitterId() != null && |
||||
|
cashRecord.getSubmitterId().equals(user.getId()); |
||||
|
if (!isSubmitter) { |
||||
|
log.warn("用户{}不是订单{}的提交人,不能重新提交", user.getId(), cashRecord.getId()); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
boolean success = cashRecord.resubmit(); |
||||
|
if (success) { |
||||
|
cashRecord.setUpdateTime(new Date()); |
||||
|
log.info("用户{}重新提交退款订单{}", user.getName(), cashRecord.getId()); |
||||
|
} |
||||
|
|
||||
|
return success; |
||||
|
} |
||||
|
|
||||
|
// 获取下一个审批节点信息 |
||||
|
public RefundApprovalNode getNextApprovalNode(CashRecord cashRecord) { |
||||
|
RefundApprovalFlowConfig flowConfig = getSuitableFlowConfig(cashRecord); |
||||
|
if (flowConfig != null) { |
||||
|
return flowConfig.getNextNode(cashRecord.getCurrentApprovalLevel()); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
// 动态添加退款审批流程配置 |
||||
|
public void addRefundFlowConfig(String marketId, RefundApprovalFlowConfig flowConfig) { |
||||
|
REFUND_APPROVAL_FLOW_CONFIG.put(marketId, flowConfig); |
||||
|
log.info("成功添加地区{}的退款审批流程配置:{}", marketId, flowConfig.getFlowName()); |
||||
|
} |
||||
|
|
||||
|
// 获取所有退款审批流程配置 |
||||
|
public Map<String, RefundApprovalFlowConfig> getAllRefundFlowConfigs() { |
||||
|
return new HashMap<>(REFUND_APPROVAL_FLOW_CONFIG); |
||||
|
} |
||||
|
|
||||
|
// 根据地区名称初始化审批流程配置 |
||||
|
public void initializeFlowForMarketName(String marketName) { |
||||
|
Market market = marketService.getMarketByName(marketName); |
||||
|
if (market != null) { |
||||
|
List<RefundApprovalNode> flowNodes = createRefundFlowForMarket(market); |
||||
|
REFUND_APPROVAL_FLOW_CONFIG.put(market.getId().toString(), |
||||
|
new RefundApprovalFlowConfig(market.getName() + "退款审批流程", flowNodes)); |
||||
|
log.info("为地区{}初始化退款审批流程配置", marketName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="com.example.demo.mapper.cash.CashRecordMapper"> |
||||
|
|
||||
|
</mapper> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue