From ee82e6e447676ae10dc41c92e5f23ac4c22d89de Mon Sep 17 00:00:00 2001 From: marsal Date: Tue, 7 Jul 2026 15:12:05 +0800 Subject: [PATCH] feat(api): add list endpoints and dashboard stats --- .../admin/controller/DashboardController.java | 60 +++++++++++++++++++ .../mci/admin/dto/DashboardStatsResponse.java | 12 ++++ .../controller/ChannelAccountController.java | 9 +++ .../controller/ChannelSubjectController.java | 9 +++ .../controller/ConversationController.java | 9 +++ .../service/ChannelAccountService.java | 10 ++++ .../service/ChannelSubjectService.java | 10 ++++ .../channel/service/ConversationService.java | 8 +++ .../mci/crm/controller/PersonController.java | 9 +++ .../sino/mci/crm/service/PersonService.java | 8 +++ .../mci/send/controller/SendController.java | 6 ++ .../sino/mci/send/service/SendService.java | 8 +++ 12 files changed, 158 insertions(+) create mode 100644 backend/mci-server/src/main/java/com/sino/mci/admin/controller/DashboardController.java create mode 100644 backend/mci-server/src/main/java/com/sino/mci/admin/dto/DashboardStatsResponse.java diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/controller/DashboardController.java b/backend/mci-server/src/main/java/com/sino/mci/admin/controller/DashboardController.java new file mode 100644 index 0000000..7b2c079 --- /dev/null +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/controller/DashboardController.java @@ -0,0 +1,60 @@ +package com.sino.mci.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.sino.mci.admin.dto.DashboardStatsResponse; +import com.sino.mci.admin.security.AuthContext; +import com.sino.mci.channel.entity.ChannelAccount; +import com.sino.mci.channel.entity.ChannelSubject; +import com.sino.mci.channel.entity.Conversation; +import com.sino.mci.channel.repository.ChannelAccountMapper; +import com.sino.mci.channel.repository.ChannelSubjectMapper; +import com.sino.mci.channel.repository.ConversationMapper; +import com.sino.mci.send.entity.SendRecord; +import com.sino.mci.send.repository.SendRecordMapper; +import com.sino.mci.shared.web.ApiResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +@RestController +@RequestMapping("/api/v1/dashboard") +@RequiredArgsConstructor +public class DashboardController { + + private final ChannelSubjectMapper subjectMapper; + private final ChannelAccountMapper accountMapper; + private final ConversationMapper conversationMapper; + private final SendRecordMapper sendRecordMapper; + + @GetMapping("/stats") + public ApiResponse stats() { + String tenantCode = AuthContext.currentTenantCode(); + LocalDateTime startOfDay = LocalDate.now().atStartOfDay(); + LocalDateTime endOfDay = LocalDate.now().atTime(LocalTime.MAX); + + DashboardStatsResponse response = new DashboardStatsResponse(); + response.setSubjectCount(subjectMapper.selectCount( + new LambdaQueryWrapper() + .eq(ChannelSubject::getTenantCode, tenantCode) + .eq(ChannelSubject::getStatus, 1))); + response.setAccountCount(accountMapper.selectCount( + new LambdaQueryWrapper() + .eq(ChannelAccount::getTenantCode, tenantCode) + .eq(ChannelAccount::getStatus, 1))); + response.setConversationCount(conversationMapper.selectCount( + new LambdaQueryWrapper() + .eq(Conversation::getTenantCode, tenantCode) + .eq(Conversation::getStatus, 1))); + response.setSentToday(sendRecordMapper.selectCount( + new LambdaQueryWrapper() + .eq(SendRecord::getTenantCode, tenantCode) + .ge(SendRecord::getSendTime, startOfDay) + .le(SendRecord::getSendTime, endOfDay))); + return ApiResponse.ok(response); + } +} diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/dto/DashboardStatsResponse.java b/backend/mci-server/src/main/java/com/sino/mci/admin/dto/DashboardStatsResponse.java new file mode 100644 index 0000000..a6f90c2 --- /dev/null +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/dto/DashboardStatsResponse.java @@ -0,0 +1,12 @@ +package com.sino.mci.admin.dto; + +import lombok.Data; + +@Data +public class DashboardStatsResponse { + + private Long subjectCount; + private Long accountCount; + private Long conversationCount; + private Long sentToday; +} diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelAccountController.java b/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelAccountController.java index f976a73..e86a33a 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelAccountController.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelAccountController.java @@ -1,13 +1,17 @@ package com.sino.mci.channel.controller; +import com.sino.mci.admin.security.AuthContext; import com.sino.mci.channel.dto.ChannelAccountInitRequest; import com.sino.mci.channel.dto.CreateChannelAccountRequest; +import com.sino.mci.channel.entity.ChannelAccount; import com.sino.mci.channel.service.ChannelAccountService; import com.sino.mci.shared.web.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api/v1/channel-account") @RequiredArgsConstructor @@ -28,4 +32,9 @@ public class ChannelAccountController { @Valid @RequestBody ChannelAccountInitRequest request) { return ApiResponse.ok(accountService.initAccount(tenantCode, request)); } + + @GetMapping("/list") + public ApiResponse> listAccounts() { + return ApiResponse.ok(accountService.listAccounts(AuthContext.currentTenantCode())); + } } diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelSubjectController.java b/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelSubjectController.java index f3b2df4..f325edf 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelSubjectController.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ChannelSubjectController.java @@ -1,12 +1,16 @@ package com.sino.mci.channel.controller; +import com.sino.mci.admin.security.AuthContext; import com.sino.mci.channel.dto.CreateChannelSubjectRequest; +import com.sino.mci.channel.entity.ChannelSubject; import com.sino.mci.channel.service.ChannelSubjectService; import com.sino.mci.shared.web.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api/v1/channel-subject") @RequiredArgsConstructor @@ -20,4 +24,9 @@ public class ChannelSubjectController { @Valid @RequestBody CreateChannelSubjectRequest request) { return ApiResponse.ok(subjectService.createSubject(tenantCode, request)); } + + @GetMapping("/list") + public ApiResponse> listSubjects() { + return ApiResponse.ok(subjectService.listSubjects(AuthContext.currentTenantCode())); + } } diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ConversationController.java b/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ConversationController.java index 3b21773..d3a940f 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ConversationController.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/controller/ConversationController.java @@ -1,14 +1,18 @@ package com.sino.mci.channel.controller; +import com.sino.mci.admin.security.AuthContext; import com.sino.mci.channel.dto.BindConversationAccountRequest; import com.sino.mci.channel.dto.BindConversationTagsRequest; import com.sino.mci.channel.dto.CreateConversationRequest; +import com.sino.mci.channel.entity.Conversation; import com.sino.mci.channel.service.ConversationService; import com.sino.mci.shared.web.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api/v1/conversation") @RequiredArgsConstructor @@ -23,6 +27,11 @@ public class ConversationController { return ApiResponse.ok(conversationService.createConversation(tenantCode, request)); } + @GetMapping("/list") + public ApiResponse> listConversations() { + return ApiResponse.ok(conversationService.listConversations(AuthContext.currentTenantCode())); + } + @GetMapping("/{conversation_id}") public ApiResponse getConversation( @RequestHeader("X-Tenant-Code") String tenantCode, diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java index dfd14d5..e5860c7 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java @@ -12,6 +12,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @RequiredArgsConstructor public class ChannelAccountService { @@ -19,6 +21,14 @@ public class ChannelAccountService { private final ChannelAccountMapper accountMapper; private final ChannelSubjectMapper subjectMapper; + public List listAccounts(String tenantCode) { + return accountMapper.selectList( + new LambdaQueryWrapper() + .eq(ChannelAccount::getTenantCode, tenantCode) + .eq(ChannelAccount::getStatus, 1) + .orderByDesc(ChannelAccount::getId)); + } + @Transactional public ChannelAccount createAccount(String tenantCode, CreateChannelAccountRequest request) { if (request.getSubjectId() != null) { diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelSubjectService.java b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelSubjectService.java index 53b8c88..b0e0806 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelSubjectService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelSubjectService.java @@ -9,12 +9,22 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @RequiredArgsConstructor public class ChannelSubjectService { private final ChannelSubjectMapper subjectMapper; + public List listSubjects(String tenantCode) { + return subjectMapper.selectList( + new LambdaQueryWrapper() + .eq(ChannelSubject::getTenantCode, tenantCode) + .eq(ChannelSubject::getStatus, 1) + .orderByDesc(ChannelSubject::getId)); + } + @Transactional public ChannelSubject createSubject(String tenantCode, CreateChannelSubjectRequest request) { ChannelSubject existing = subjectMapper.selectOne( diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ConversationService.java b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ConversationService.java index b45c6ed..05fe45e 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ConversationService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ConversationService.java @@ -31,6 +31,14 @@ public class ConversationService { private final ConversationTagBindingMapper conversationTagBindingMapper; private final TagMapper tagMapper; + public List listConversations(String tenantCode) { + return conversationMapper.selectList( + new LambdaQueryWrapper() + .eq(Conversation::getTenantCode, tenantCode) + .eq(Conversation::getStatus, 1) + .orderByDesc(Conversation::getId)); + } + @Transactional public Conversation createConversation(String tenantCode, CreateConversationRequest request) { Conversation existing = conversationMapper.selectOne( diff --git a/backend/mci-server/src/main/java/com/sino/mci/crm/controller/PersonController.java b/backend/mci-server/src/main/java/com/sino/mci/crm/controller/PersonController.java index 2deda72..7c92854 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/crm/controller/PersonController.java +++ b/backend/mci-server/src/main/java/com/sino/mci/crm/controller/PersonController.java @@ -1,14 +1,18 @@ package com.sino.mci.crm.controller; +import com.sino.mci.admin.security.AuthContext; import com.sino.mci.crm.dto.BindTagsRequest; import com.sino.mci.crm.dto.CreateIdentityRequest; import com.sino.mci.crm.dto.CreatePersonRequest; +import com.sino.mci.crm.entity.Person; import com.sino.mci.crm.service.PersonService; import com.sino.mci.shared.web.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api/v1/person") @RequiredArgsConstructor @@ -23,6 +27,11 @@ public class PersonController { return ApiResponse.ok(personService.createPerson(tenantCode, request)); } + @GetMapping("/list") + public ApiResponse> listPersons() { + return ApiResponse.ok(personService.listPersons(AuthContext.currentTenantCode())); + } + @GetMapping("/{person_id}") public ApiResponse getPerson( @RequestHeader("X-Tenant-Code") String tenantCode, diff --git a/backend/mci-server/src/main/java/com/sino/mci/crm/service/PersonService.java b/backend/mci-server/src/main/java/com/sino/mci/crm/service/PersonService.java index 98fc3e3..23fb464 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/crm/service/PersonService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/crm/service/PersonService.java @@ -31,6 +31,14 @@ public class PersonService { private final PersonTagBindingMapper personTagBindingMapper; private final TagMapper tagMapper; + public List listPersons(String tenantCode) { + return personMapper.selectList( + new LambdaQueryWrapper() + .eq(Person::getTenantCode, tenantCode) + .eq(Person::getStatus, 1) + .orderByDesc(Person::getId)); + } + @Transactional public Person createPerson(String tenantCode, CreatePersonRequest request) { Person person = new Person(); diff --git a/backend/mci-server/src/main/java/com/sino/mci/send/controller/SendController.java b/backend/mci-server/src/main/java/com/sino/mci/send/controller/SendController.java index 5e31089..c44ad0c 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/send/controller/SendController.java +++ b/backend/mci-server/src/main/java/com/sino/mci/send/controller/SendController.java @@ -1,5 +1,6 @@ package com.sino.mci.send.controller; +import com.sino.mci.admin.security.AuthContext; import com.sino.mci.send.dto.SendRecordResponse; import com.sino.mci.send.dto.SendRequest; import com.sino.mci.send.dto.SendResponse; @@ -39,4 +40,9 @@ public class SendController { @PathVariable("task_id") String taskId) { return ApiResponse.ok(sendService.listRecords(tenantCode, taskId)); } + + @GetMapping("/list") + public ApiResponse> listRecentRecords() { + return ApiResponse.ok(sendService.listRecentRecords(AuthContext.currentTenantCode())); + } } diff --git a/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java b/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java index e979fcb..969ecee 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java @@ -101,6 +101,14 @@ public class SendService { return records.stream().map(this::toResponse).collect(Collectors.toList()); } + public List listRecentRecords(String tenantCode) { + List records = sendRecordMapper.selectList( + new LambdaQueryWrapper() + .eq(SendRecord::getTenantCode, tenantCode) + .orderByDesc(SendRecord::getId)); + return records.stream().limit(50).map(this::toResponse).collect(Collectors.toList()); + } + private String generateTaskId() { return "SND" + UUID.randomUUID().toString().replace("-", "").toUpperCase(); }