接入运营商提现开票待办闭环

This commit is contained in:
jsowell
2026-07-17 17:48:14 +08:00
parent bc20330f1b
commit 3e299f40f9
14 changed files with 920 additions and 16 deletions

View File

@@ -124,6 +124,9 @@ public class OrderService {
@Autowired
private ClearingWithdrawInfoService clearingWithdrawInfoService;
@Autowired
private MerchantWithdrawInvoiceService merchantWithdrawInvoiceService;
@Autowired
private MemberAdapayRecordService memberAdapayRecordService;
@@ -1464,22 +1467,7 @@ public class OrderService {
log.info("取现成功 data:{}", JSON.toJSONString(data));
JSONObject jsonObject = JSON.parseObject(data);
String withdrawCode = jsonObject.getString("id");
// 通过取现id查询取现数据
ClearingWithdrawInfo clearingWithdrawInfo = clearingWithdrawInfoService.selectByWithdrawCode(withdrawCode);
if (clearingWithdrawInfo != null) {
clearingWithdrawInfo.setWithdrawStatus(Constants.ONE);
clearingWithdrawInfo.setUpdateTime(DateUtils.getNowDate());
clearingWithdrawInfoService.updateByPrimaryKeySelective(clearingWithdrawInfo);
}
// 清分账单数据更新
List<ClearingBillInfo> clearingBillInfos = clearingBillInfoService.selectByWithdrawCode(withdrawCode);
if (CollectionUtils.isNotEmpty(clearingBillInfos)) {
for (ClearingBillInfo clearingBillInfo : clearingBillInfos) {
clearingBillInfo.setBillStatus("4");
}
clearingBillInfoService.updateBatchSelective(clearingBillInfos);
}
merchantWithdrawInvoiceService.handleWithdrawSucceeded(withdrawCode);
}
/**

View File

@@ -0,0 +1,54 @@
package com.jsowell.web.controller.pile;
import com.jsowell.common.annotation.Log;
import com.jsowell.common.core.controller.BaseController;
import com.jsowell.common.core.domain.AjaxResult;
import com.jsowell.common.enums.BusinessType;
import com.jsowell.pile.dto.WithdrawInvoiceCloseRequest;
import com.jsowell.pile.dto.WithdrawInvoiceSubmitRequest;
import com.jsowell.pile.service.MerchantWithdrawInvoiceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 运营商提现向平台开票接口。
*/
@RestController
@RequestMapping("/pile/withdrawInvoice")
public class MerchantWithdrawInvoiceController extends BaseController {
@Autowired
private MerchantWithdrawInvoiceService merchantWithdrawInvoiceService;
@PreAuthorize("@ss.hasPermi('system:todo:query')")
@GetMapping("/{withdrawCode}")
public AjaxResult getInfo(@PathVariable String withdrawCode, @RequestParam Long todoId) {
return AjaxResult.success(merchantWithdrawInvoiceService.getForAssignee(
withdrawCode, todoId, getUserId()));
}
@PreAuthorize("@ss.hasPermi('system:todo:complete')")
@Log(title = "运营商提现开票", businessType = BusinessType.UPDATE)
@PutMapping("/{withdrawCode}/submit")
public AjaxResult submit(@PathVariable String withdrawCode,
@Validated @RequestBody WithdrawInvoiceSubmitRequest request) {
return toAjax(merchantWithdrawInvoiceService.submit(withdrawCode, request,
getUserId(), getUsername()));
}
@PreAuthorize("@ss.hasPermi('system:todo:cancel')")
@Log(title = "运营商提现开票", businessType = BusinessType.UPDATE)
@PutMapping("/{withdrawCode}/close")
public AjaxResult close(@PathVariable String withdrawCode,
@Validated @RequestBody WithdrawInvoiceCloseRequest request) {
return toAjax(merchantWithdrawInvoiceService.close(withdrawCode, request.getReason(),
getUserId(), getUsername()));
}
}

View File

@@ -0,0 +1,174 @@
package com.jsowell.pile.service;
import com.jsowell.common.constant.Constants;
import com.jsowell.pile.constant.MerchantWithdrawInvoiceConstants;
import com.jsowell.pile.domain.ClearingBillInfo;
import com.jsowell.pile.domain.ClearingWithdrawInfo;
import com.jsowell.pile.domain.ClearingWithdrawInvoice;
import com.jsowell.pile.domain.PileMerchantInfo;
import com.jsowell.pile.dto.WithdrawInvoiceSubmitRequest;
import com.jsowell.pile.mapper.ClearingWithdrawInfoMapper;
import com.jsowell.pile.mapper.ClearingWithdrawInvoiceMapper;
import com.jsowell.pile.service.impl.MerchantWithdrawInvoiceServiceImpl;
import com.jsowell.system.domain.SysTodoTask;
import com.jsowell.system.domain.dto.TodoTaskCreateCommand;
import com.jsowell.system.service.TodoTaskAssigneeService;
import com.jsowell.system.service.TodoTaskService;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class MerchantWithdrawInvoiceServiceImplTest {
@Test
void handleSucceeded_shouldUpdateWithdrawAndCreateAdminTodos() {
Dependencies dependencies = new Dependencies();
MerchantWithdrawInvoiceServiceImpl service = dependencies.createService();
ClearingWithdrawInfo withdrawInfo = withdrawInfo(Constants.ZERO);
when(dependencies.withdrawInfoMapper.selectByWithdrawCode("W100")).thenReturn(withdrawInfo);
when(dependencies.invoiceMapper.selectByWithdrawCode("W100")).thenReturn(null);
when(dependencies.invoiceMapper.insertSelective(any(ClearingWithdrawInvoice.class))).thenReturn(1);
ClearingBillInfo billInfo = new ClearingBillInfo();
billInfo.setId(5);
when(dependencies.clearingBillInfoService.selectByWithdrawCode("W100"))
.thenReturn(Collections.singletonList(billInfo));
PileMerchantInfo merchantInfo = new PileMerchantInfo();
merchantInfo.setDeptId("200");
when(dependencies.pileMerchantInfoService.selectPileMerchantInfoById(100L)).thenReturn(merchantInfo);
when(dependencies.assigneeService.findActiveOperatorAdminUserIdsByDeptTree(200L))
.thenReturn(Arrays.asList(8L, 9L));
service.handleWithdrawSucceeded("W100");
verify(dependencies.withdrawInfoMapper).updateByPrimaryKeySelective(withdrawInfo);
assertEquals(Constants.ONE, withdrawInfo.getWithdrawStatus());
assertEquals("4", billInfo.getBillStatus());
verify(dependencies.clearingBillInfoService).updateBatchSelective(Collections.singletonList(billInfo));
ArgumentCaptor<TodoTaskCreateCommand> captor = ArgumentCaptor.forClass(TodoTaskCreateCommand.class);
verify(dependencies.todoTaskService, times(2)).createTask(captor.capture());
assertEquals(Arrays.asList(8L, 9L), Arrays.asList(
captor.getAllValues().get(0).getAssigneeUserId(),
captor.getAllValues().get(1).getAssigneeUserId()));
assertEquals(MerchantWithdrawInvoiceConstants.TASK_TYPE, captor.getValue().getTaskType());
assertEquals(MerchantWithdrawInvoiceConstants.BUSINESS_TYPE, captor.getValue().getBusinessType());
assertEquals("financeDetail", captor.getValue().getRouteName());
}
@Test
void handleSucceeded_shouldNotRecreateTodoAfterInvoiceSubmitted() {
Dependencies dependencies = new Dependencies();
MerchantWithdrawInvoiceServiceImpl service = dependencies.createService();
when(dependencies.withdrawInfoMapper.selectByWithdrawCode("W100"))
.thenReturn(withdrawInfo(Constants.ONE));
ClearingWithdrawInvoice invoice = invoice(MerchantWithdrawInvoiceConstants.STATUS_SUBMITTED);
when(dependencies.invoiceMapper.selectByWithdrawCode("W100")).thenReturn(invoice);
when(dependencies.clearingBillInfoService.selectByWithdrawCode("W100"))
.thenReturn(Collections.emptyList());
service.handleWithdrawSucceeded("W100");
verify(dependencies.todoTaskService, never()).createTask(any(TodoTaskCreateCommand.class));
}
@Test
void submit_shouldCompleteAllBusinessTodos() {
Dependencies dependencies = new Dependencies();
MerchantWithdrawInvoiceServiceImpl service = dependencies.createService();
when(dependencies.todoTaskService.getForAssignee(20L, 8L)).thenReturn(todoTask());
when(dependencies.invoiceMapper.selectByWithdrawCode("W100"))
.thenReturn(invoice(MerchantWithdrawInvoiceConstants.STATUS_PENDING));
when(dependencies.invoiceMapper.markSubmitted(any(ClearingWithdrawInvoice.class))).thenReturn(1);
WithdrawInvoiceSubmitRequest request = new WithdrawInvoiceSubmitRequest();
request.setTodoId(20L);
request.setInvoiceNumber("INV-100");
request.setVoucherUrl("/profile/upload/invoice.pdf");
assertTrue(service.submit("W100", request, 8L, "operator"));
verify(dependencies.todoTaskService).completeByBusiness(
MerchantWithdrawInvoiceConstants.BUSINESS_TYPE, "W100", null, 8L, "operator");
}
@Test
void close_shouldCancelAllBusinessTodos() {
Dependencies dependencies = new Dependencies();
MerchantWithdrawInvoiceServiceImpl service = dependencies.createService();
when(dependencies.invoiceMapper.selectByWithdrawCode("W100"))
.thenReturn(invoice(MerchantWithdrawInvoiceConstants.STATUS_PENDING));
when(dependencies.invoiceMapper.markClosed(any(ClearingWithdrawInvoice.class))).thenReturn(1);
assertTrue(service.close("W100", "无需开票", 1L, "admin"));
verify(dependencies.todoTaskService).cancelByBusiness(
MerchantWithdrawInvoiceConstants.BUSINESS_TYPE, "W100", null, "admin", "无需开票");
}
private static ClearingWithdrawInfo withdrawInfo(String status) {
ClearingWithdrawInfo withdrawInfo = new ClearingWithdrawInfo();
withdrawInfo.setId(1);
withdrawInfo.setMerchantId("100");
withdrawInfo.setWithdrawCode("W100");
withdrawInfo.setWithdrawStatus(status);
return withdrawInfo;
}
private static ClearingWithdrawInvoice invoice(String status) {
ClearingWithdrawInvoice invoice = new ClearingWithdrawInvoice();
invoice.setId(10L);
invoice.setMerchantId("100");
invoice.setWithdrawCode("W100");
invoice.setInvoiceStatus(status);
return invoice;
}
private static SysTodoTask todoTask() {
SysTodoTask task = new SysTodoTask();
task.setTodoId(20L);
task.setTaskType(MerchantWithdrawInvoiceConstants.TASK_TYPE);
task.setBusinessType(MerchantWithdrawInvoiceConstants.BUSINESS_TYPE);
task.setBusinessId("W100");
task.setTaskStatus("PENDING");
return task;
}
private static void setField(Object target, String fieldName, Object value) {
try {
Field field = MerchantWithdrawInvoiceServiceImpl.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
private static class Dependencies {
private final ClearingWithdrawInfoMapper withdrawInfoMapper = mock(ClearingWithdrawInfoMapper.class);
private final ClearingWithdrawInvoiceMapper invoiceMapper = mock(ClearingWithdrawInvoiceMapper.class);
private final ClearingBillInfoService clearingBillInfoService = mock(ClearingBillInfoService.class);
private final PileMerchantInfoService pileMerchantInfoService = mock(PileMerchantInfoService.class);
private final TodoTaskAssigneeService assigneeService = mock(TodoTaskAssigneeService.class);
private final TodoTaskService todoTaskService = mock(TodoTaskService.class);
private MerchantWithdrawInvoiceServiceImpl createService() {
MerchantWithdrawInvoiceServiceImpl service = new MerchantWithdrawInvoiceServiceImpl();
setField(service, "clearingWithdrawInfoMapper", withdrawInfoMapper);
setField(service, "clearingWithdrawInvoiceMapper", invoiceMapper);
setField(service, "clearingBillInfoService", clearingBillInfoService);
setField(service, "pileMerchantInfoService", pileMerchantInfoService);
setField(service, "todoTaskAssigneeService", assigneeService);
setField(service, "todoTaskService", todoTaskService);
return service;
}
}
}

View File

@@ -0,0 +1,78 @@
package com.jsowell.web.controller.pile;
import com.jsowell.common.core.domain.AjaxResult;
import com.jsowell.pile.dto.WithdrawInvoiceSubmitRequest;
import com.jsowell.pile.service.MerchantWithdrawInvoiceService;
import com.jsowell.pile.vo.web.MerchantWithdrawInvoiceVO;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class MerchantWithdrawInvoiceControllerTest {
@Test
void getInfo_shouldUseCurrentLoginUser() {
MerchantWithdrawInvoiceService service = mock(MerchantWithdrawInvoiceService.class);
TestController controller = newController(service, 8L, "operator");
MerchantWithdrawInvoiceVO view = new MerchantWithdrawInvoiceVO();
when(service.getForAssignee("W100", 20L, 8L)).thenReturn(view);
AjaxResult result = controller.getInfo("W100", 20L);
assertSame(view, result.get(AjaxResult.DATA_TAG));
verify(service).getForAssignee("W100", 20L, 8L);
}
@Test
void submit_shouldPassCurrentUserAndOperator() {
MerchantWithdrawInvoiceService service = mock(MerchantWithdrawInvoiceService.class);
TestController controller = newController(service, 8L, "operator");
WithdrawInvoiceSubmitRequest request = new WithdrawInvoiceSubmitRequest();
request.setTodoId(20L);
request.setInvoiceNumber("INV-100");
request.setVoucherUrl("/invoice.pdf");
when(service.submit("W100", request, 8L, "operator")).thenReturn(true);
controller.submit("W100", request);
verify(service).submit("W100", request, 8L, "operator");
}
private static TestController newController(MerchantWithdrawInvoiceService service,
Long userId, String username) {
TestController controller = new TestController(userId, username);
try {
Field field = MerchantWithdrawInvoiceController.class
.getDeclaredField("merchantWithdrawInvoiceService");
field.setAccessible(true);
field.set(controller, service);
} catch (Exception exception) {
throw new RuntimeException(exception);
}
return controller;
}
private static class TestController extends MerchantWithdrawInvoiceController {
private final Long userId;
private final String username;
private TestController(Long userId, String username) {
this.userId = userId;
this.username = username;
}
@Override
public Long getUserId() {
return userId;
}
@Override
public String getUsername() {
return username;
}
}
}