v1.0.3 #7
@@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>com.imyeyu.timiserverapi</groupId>
|
||||
<artifactId>TimiServerAPI</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.3</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>TimiServerAPI</name>
|
||||
<description>imyeyu.com API</description>
|
||||
@@ -178,7 +178,7 @@
|
||||
<dependency>
|
||||
<groupId>com.imyeyu.network</groupId>
|
||||
<artifactId>timi-network</artifactId>
|
||||
<version>0.0.12</version>
|
||||
<version>0.0.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.imyeyu.lang</groupId>
|
||||
@@ -323,6 +323,11 @@
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.18.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
|
||||
@@ -17,9 +17,11 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -27,6 +29,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -67,6 +71,26 @@ public class GaoCustomerController {
|
||||
return service.search(req);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
|
||||
@PostMapping("/search/page")
|
||||
public PageResult<GaoCustomer> searchPage(@RequestBody Page<GaoCustomerSearchReq> page) {
|
||||
return service.searchPage(page);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@IgnoreGlobalReturn
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
|
||||
@PostMapping("/export")
|
||||
public void export(@RequestBody GaoCustomerSearchReq req, HttpServletResponse resp) throws IOException {
|
||||
String fileName = URLEncoder.encode("客户列表.xlsx", "UTF-8").replace("+", "%20");
|
||||
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
resp.setHeader("Content-Disposition", "attachment; filename=\"gao-customer.xlsx\"; filename*=UTF-8''" + fileName);
|
||||
resp.getOutputStream().write(service.exportSearchExcel(req));
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
|
||||
@@ -95,8 +119,9 @@ public class GaoCustomerController {
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_CREATE)
|
||||
@PostMapping("/create")
|
||||
public void create(@RequestBody GaoCustomer customer) {
|
||||
public GaoCustomer create(@RequestBody GaoCustomer customer) {
|
||||
service.create(customer);
|
||||
return customer;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
|
||||
+13
-31
@@ -29,7 +29,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
///
|
||||
@@ -53,6 +53,14 @@ public class GaoRegistrationEventRecordController {
|
||||
return service.page(page);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
|
||||
@PostMapping("/range/page")
|
||||
public PageResult<GaoRegistrationEventRecord> rangePage(@RequestBody Page<GaoRegistrationEventRecordRangeReq> page) {
|
||||
return service.pageByRange(page);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
|
||||
@@ -106,10 +114,10 @@ public class GaoRegistrationEventRecordController {
|
||||
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
|
||||
@PostMapping("/range/export")
|
||||
public void rangeExport(@RequestBody GaoRegistrationEventRecordRangeReq req, HttpServletResponse resp) throws IOException {
|
||||
List<GaoRegistrationEventRecord> list = service.listByRange(req);
|
||||
resp.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||
resp.setHeader("Content-Disposition", "attachment; filename=\"gao-registration-event-record.csv\"");
|
||||
resp.getOutputStream().write(buildCsv(list).getBytes(StandardCharsets.UTF_8));
|
||||
String fileName = URLEncoder.encode("登记事件记录.xlsx", "UTF-8").replace("+", "%20");
|
||||
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
resp.setHeader("Content-Disposition", "attachment; filename=\"gao-registration-event-record.xlsx\"; filename*=UTF-8''" + fileName);
|
||||
resp.getOutputStream().write(service.exportRangeExcel(req));
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@@ -163,30 +171,4 @@ public class GaoRegistrationEventRecordController {
|
||||
public List<GaoRegistrationEventRecordDailyStatView> allCustomerRegistrationEventDailyStat(@RequestBody GaoRegistrationEventRecordTrendStatReq req) {
|
||||
return service.statDailyByRegistrationEvent(req);
|
||||
}
|
||||
|
||||
private String buildCsv(List<GaoRegistrationEventRecord> list) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append((char) 65279);
|
||||
builder.append("客户编码,客户姓名,登记事件,登记时间,登记人用户 ID,备注\n");
|
||||
for (GaoRegistrationEventRecord record : list) {
|
||||
builder.append(csv(record.getCustomer() == null ? null : record.getCustomer().getCode())).append(',');
|
||||
builder.append(csv(record.getCustomer() == null ? null : record.getCustomer().getName())).append(',');
|
||||
builder.append(csv(record.getRegistrationEvent() == null ? null : record.getRegistrationEvent().getName())).append(',');
|
||||
builder.append(csv(record.getRegisteredAt())).append(',');
|
||||
builder.append(csv(record.getOperatorUserId())).append(',');
|
||||
builder.append(csv(record.getRemark())).append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String csv(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String text = value.toString();
|
||||
if (!text.contains(",") && !text.contains("\"") && !text.contains("\n") && !text.contains("\r")) {
|
||||
return text;
|
||||
}
|
||||
return "\"%s\"".formatted(text.replace("\"", "\"\""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
|
||||
|
||||
List<GaoCustomer> search(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId);
|
||||
|
||||
Long countSearch(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId);
|
||||
|
||||
List<GaoCustomer> searchPage(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId, @Param("offset") Long offset, @Param("size") Long size);
|
||||
|
||||
List<GaoCustomer> selectInvitedList(@Param("introducerCustomerId") String introducerCustomerId);
|
||||
|
||||
Long countInvited(@Param("introducerCustomerId") String introducerCustomerId);
|
||||
|
||||
@@ -103,4 +103,18 @@ public interface GaoRegistrationEventRecordMapper extends BaseMapper<GaoRegistra
|
||||
/// @param req 范围查询请求
|
||||
/// @return 登记事件记录列表
|
||||
List<GaoRegistrationEventRecord> selectByRange(@Param("req") GaoRegistrationEventRecordRangeReq req);
|
||||
|
||||
/// 按任意时间范围统计登记事件记录
|
||||
///
|
||||
/// @param req 范围查询请求
|
||||
/// @return 登记事件记录数
|
||||
Long countByRange(@Param("req") GaoRegistrationEventRecordRangeReq req);
|
||||
|
||||
/// 按任意时间范围分页查询登记事件记录
|
||||
///
|
||||
/// @param req 范围查询请求
|
||||
/// @param offset 偏移量
|
||||
/// @param size 每页条数
|
||||
/// @return 登记事件记录列表
|
||||
List<GaoRegistrationEventRecord> selectPageByRange(@Param("req") GaoRegistrationEventRecordRangeReq req, @Param("offset") Long offset, @Param("size") Long size);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
///
|
||||
@@ -40,6 +43,18 @@ public interface GaoCustomerService extends BaseService<GaoCustomer, String> {
|
||||
/// @return 客户列表
|
||||
List<GaoCustomer> search(GaoCustomerSearchReq req);
|
||||
|
||||
/// 分页搜索客户
|
||||
///
|
||||
/// @param page 分页搜索请求
|
||||
/// @return 客户分页结果
|
||||
PageResult<GaoCustomer> searchPage(Page<GaoCustomerSearchReq> page);
|
||||
|
||||
/// 按搜索条件导出客户 Excel
|
||||
///
|
||||
/// @param req 搜索请求
|
||||
/// @return xlsx 文件字节
|
||||
byte[] exportSearchExcel(GaoCustomerSearchReq req) throws IOException;
|
||||
|
||||
/// 查询邀请的客户
|
||||
///
|
||||
/// @param introducerCustomerId 介绍人客户 ID
|
||||
|
||||
+15
@@ -10,8 +10,11 @@ import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordTrendStatReq;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
///
|
||||
@@ -39,6 +42,18 @@ public interface GaoRegistrationEventRecordService extends BaseService<GaoRegist
|
||||
/// @return 登记事件记录列表
|
||||
List<GaoRegistrationEventRecord> listByRange(GaoRegistrationEventRecordRangeReq req);
|
||||
|
||||
/// 按任意时间范围分页查询登记事件记录
|
||||
///
|
||||
/// @param page 分页参数
|
||||
/// @return 登记事件记录分页结果
|
||||
PageResult<GaoRegistrationEventRecord> pageByRange(Page<GaoRegistrationEventRecordRangeReq> page);
|
||||
|
||||
/// 按任意时间范围导出登记事件记录 Excel
|
||||
///
|
||||
/// @param req 范围查询请求
|
||||
/// @return xlsx 文件字节
|
||||
byte[] exportRangeExcel(GaoRegistrationEventRecordRangeReq req) throws IOException;
|
||||
|
||||
/// 按周期查询登记事件记录
|
||||
///
|
||||
/// @param req 周期请求
|
||||
|
||||
+115
-7
@@ -14,12 +14,22 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@@ -40,6 +50,7 @@ import java.util.Map;
|
||||
public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustomer, String> implements GaoCustomerService {
|
||||
|
||||
private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
|
||||
|
||||
private final GaoCustomerMapper mapper;
|
||||
private final AttachmentService attachmentService;
|
||||
@@ -104,14 +115,75 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
|
||||
@Override
|
||||
public List<GaoCustomer> search(GaoCustomerSearchReq req) {
|
||||
if (req == null) {
|
||||
req = new GaoCustomerSearchReq();
|
||||
}
|
||||
req = resolveSearchReq(req);
|
||||
List<GaoCustomer> list = mapper.search(req.getKeyword(), req.getIntroducerCustomerId());
|
||||
list.forEach(this::loadTransient);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoCustomer> searchPage(Page<GaoCustomerSearchReq> page) {
|
||||
TimiException.required(page, "not found page");
|
||||
TimiException.required(page.getIndex(), "not found page.index");
|
||||
TimiException.required(page.getSize(), "not found page.size");
|
||||
TimiException.requiredTrue(0 <= page.getIndex(), "page.index lt 0");
|
||||
TimiException.requiredTrue(0 < page.getSize(), "page.size lte 0");
|
||||
GaoCustomerSearchReq req = resolveSearchReq(page.getEqualsExample());
|
||||
Long total = TimiJava.defaultIfNull(mapper.countSearch(req.getKeyword(), req.getIntroducerCustomerId()), 0L);
|
||||
List<GaoCustomer> list = mapper.searchPage(req.getKeyword(), req.getIntroducerCustomerId(), page.getIndex() * page.getSize(), page.getSize());
|
||||
list.forEach(this::loadTransient);
|
||||
PageResult<GaoCustomer> result = new PageResult<>();
|
||||
result.setTotal(total);
|
||||
result.setList(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportSearchExcel(GaoCustomerSearchReq req) throws IOException {
|
||||
List<GaoCustomer> list = search(req);
|
||||
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
Sheet sheet = workbook.createSheet("客户列表");
|
||||
CellStyle titleStyle = workbook.createCellStyle();
|
||||
Font titleFont = workbook.createFont();
|
||||
titleFont.setBold(true);
|
||||
titleFont.setFontHeightInPoints((short) 14);
|
||||
titleStyle.setFont(titleFont);
|
||||
CellStyle headerStyle = workbook.createCellStyle();
|
||||
Font headerFont = workbook.createFont();
|
||||
headerFont.setBold(true);
|
||||
headerStyle.setFont(headerFont);
|
||||
|
||||
appendRow(sheet, 0, titleStyle, "客户列表导出");
|
||||
appendRow(sheet, 1, null, "生成时间", EXPORT_TIME_FORMATTER.format(Instant.now()));
|
||||
appendRow(sheet, 2, null, "关键词", req == null || req.getKeyword() == null || req.getKeyword().isBlank() ? "全部" : req.getKeyword().trim());
|
||||
appendRow(sheet, 3, null, "记录总数", String.valueOf(list.size()));
|
||||
appendRow(sheet, 5, headerStyle, "客户编码", "姓名", "性别", "年龄", "电话", "地址", "病症", "备注", "邀请人", "邀请客户数", "创建时间");
|
||||
int rowIndex = 6;
|
||||
for (GaoCustomer customer : list) {
|
||||
appendRow(sheet, rowIndex, null,
|
||||
text(customer.getCode()),
|
||||
text(customer.getName()),
|
||||
formatGender(customer),
|
||||
customer.getAge() == null ? "" : String.valueOf(customer.getAge()),
|
||||
text(customer.getTelephone()),
|
||||
text(customer.getAddress()),
|
||||
text(customer.getDisease()),
|
||||
text(customer.getRemark()),
|
||||
customer.getIntroducerCustomer() == null ? "" : text(customer.getIntroducerCustomer().getName()),
|
||||
customer.getInvitedCount() == null ? "0" : String.valueOf(customer.getInvitedCount()),
|
||||
formatExportTime(customer.getCreatedAt())
|
||||
);
|
||||
rowIndex++;
|
||||
}
|
||||
for (int index = 0; index < 11; index++) {
|
||||
sheet.autoSizeColumn(index);
|
||||
sheet.setColumnWidth(index, Math.min(Math.max(sheet.getColumnWidth(index), 10 * 256), 36 * 256));
|
||||
}
|
||||
workbook.write(output);
|
||||
return output.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoCustomer> listInvited(String introducerCustomerId) {
|
||||
TimiException.required(introducerCustomerId, "not found introducerCustomerId");
|
||||
@@ -176,16 +248,21 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
TimiException.requiredTrue(days <= 366, "daily stat range too large");
|
||||
}
|
||||
|
||||
private GaoCustomerSearchReq resolveSearchReq(GaoCustomerSearchReq req) {
|
||||
return req == null ? new GaoCustomerSearchReq() : req;
|
||||
}
|
||||
|
||||
private void checkEntity(GaoCustomer entity, boolean requireId) {
|
||||
TimiException.required(entity, "not found customer");
|
||||
if (requireId) {
|
||||
TimiException.required(entity.getId(), "not found customer.id");
|
||||
}
|
||||
TimiException.required(entity.getCode(), "not found customer.code");
|
||||
TimiException.required(entity.getName(), "not found customer.name");
|
||||
GaoCustomer dbCustomer = mapper.selectByCode(entity.getCode());
|
||||
if (dbCustomer != null && !dbCustomer.getId().equals(entity.getId())) {
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("客户编码已存在");
|
||||
if (TimiJava.isNotEmpty(entity.getCode())) {
|
||||
GaoCustomer dbCustomer = mapper.selectByCode(entity.getCode());
|
||||
if (dbCustomer != null && !dbCustomer.getId().equals(entity.getId())) {
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("客户编码已存在");
|
||||
}
|
||||
}
|
||||
if (TimiJava.isNotEmpty(entity.getIntroducerCustomerId())) {
|
||||
TimiException.requiredTrue(!entity.getIntroducerCustomerId().equals(entity.getId()), "customer.introducerCustomerId invalid");
|
||||
@@ -240,4 +317,35 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void appendRow(Sheet sheet, int rowIndex, CellStyle style, String... values) {
|
||||
Row row = sheet.createRow(rowIndex);
|
||||
for (int index = 0; index < values.length; index++) {
|
||||
row.createCell(index).setCellValue(values[index]);
|
||||
if (style != null) {
|
||||
row.getCell(index).setCellStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String formatGender(GaoCustomer customer) {
|
||||
if (customer.getGender() == null) {
|
||||
return "";
|
||||
}
|
||||
return switch (customer.getGender()) {
|
||||
case MALE -> "男";
|
||||
case FEMALE -> "女";
|
||||
};
|
||||
}
|
||||
|
||||
private String formatExportTime(Long value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return EXPORT_TIME_FORMATTER.format(Instant.ofEpochMilli(value));
|
||||
}
|
||||
|
||||
private String text(Object value) {
|
||||
return value == null ? "" : value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
+123
@@ -26,13 +26,23 @@ import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.api.modules.user.service.UserService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@@ -43,6 +53,8 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
///
|
||||
/// 登记事件记录服务实现
|
||||
@@ -54,6 +66,7 @@ import java.util.Map;
|
||||
public class GaoRegistrationEventRecordServiceImplement extends AbstractEntityService<GaoRegistrationEventRecord, String> implements GaoRegistrationEventRecordService {
|
||||
|
||||
private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
|
||||
|
||||
private final GaoRegistrationEventRecordMapper mapper;
|
||||
private final AttachmentService attachmentService;
|
||||
@@ -148,6 +161,67 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoRegistrationEventRecord> pageByRange(Page<GaoRegistrationEventRecordRangeReq> page) {
|
||||
TimiException.required(page, "not found page");
|
||||
TimiException.required(page.getIndex(), "not found page.index");
|
||||
TimiException.required(page.getSize(), "not found page.size");
|
||||
TimiException.requiredTrue(0 <= page.getIndex(), "page.index lt 0");
|
||||
TimiException.requiredTrue(0 < page.getSize(), "page.size lte 0");
|
||||
GaoRegistrationEventRecordRangeReq req = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoRegistrationEventRecordRangeReq());
|
||||
checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt());
|
||||
Long total = TimiJava.defaultIfNull(mapper.countByRange(req), 0L);
|
||||
List<GaoRegistrationEventRecord> list = mapper.selectPageByRange(req, page.getIndex() * page.getSize(), page.getSize());
|
||||
list.forEach(this::loadTransient);
|
||||
PageResult<GaoRegistrationEventRecord> result = new PageResult<>();
|
||||
result.setTotal(total);
|
||||
result.setList(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportRangeExcel(GaoRegistrationEventRecordRangeReq req) throws IOException {
|
||||
List<GaoRegistrationEventRecord> list = listByRange(req);
|
||||
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
Sheet sheet = workbook.createSheet("登记事件记录");
|
||||
CellStyle titleStyle = workbook.createCellStyle();
|
||||
Font titleFont = workbook.createFont();
|
||||
titleFont.setBold(true);
|
||||
titleFont.setFontHeightInPoints((short) 14);
|
||||
titleStyle.setFont(titleFont);
|
||||
CellStyle headerStyle = workbook.createCellStyle();
|
||||
Font headerFont = workbook.createFont();
|
||||
headerFont.setBold(true);
|
||||
headerStyle.setFont(headerFont);
|
||||
|
||||
appendRow(sheet, 0, titleStyle, "登记事件记录导出");
|
||||
appendRow(sheet, 1, null, "生成时间", EXPORT_TIME_FORMATTER.format(Instant.now()));
|
||||
appendRow(sheet, 2, null, "时间范围", "%s ~ %s".formatted(formatExportTime(req.getBeginRegisteredAt()), formatExportTime(req.getEndRegisteredAt())));
|
||||
appendRow(sheet, 3, null, "事件筛选", buildEventFilterText(req));
|
||||
appendRow(sheet, 4, null, "关键词", req.getKeyword() == null || req.getKeyword().isBlank() ? "全部" : req.getKeyword().trim());
|
||||
appendRow(sheet, 5, null, "记录总数", String.valueOf(list.size()));
|
||||
appendRow(sheet, 7, headerStyle, "客户编码", "客户姓名", "登记事件", "登记时间", "登记人", "备注");
|
||||
int rowIndex = 8;
|
||||
for (GaoRegistrationEventRecord record : list) {
|
||||
appendRow(sheet, rowIndex, null,
|
||||
record.getCustomer() == null ? "" : text(record.getCustomer().getCode()),
|
||||
record.getCustomer() == null ? "" : text(record.getCustomer().getName()),
|
||||
record.getRegistrationEvent() == null ? "" : text(record.getRegistrationEvent().getName()),
|
||||
formatExportTime(record.getRegisteredAt()),
|
||||
getOperatorName(record),
|
||||
text(record.getRemark())
|
||||
);
|
||||
rowIndex++;
|
||||
}
|
||||
for (int index = 0; index < 6; index++) {
|
||||
sheet.autoSizeColumn(index);
|
||||
sheet.setColumnWidth(index, Math.min(Math.max(sheet.getColumnWidth(index), 12 * 256), 40 * 256));
|
||||
}
|
||||
workbook.write(output);
|
||||
return output.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoRegistrationEventPeriodStatView> statByRegistrationEventAndPeriodType(String registrationEventId, GaoRegistrationEventRecordPeriodReq.PeriodType periodType) {
|
||||
TimiException.required(registrationEventId, "not found registrationEventId");
|
||||
@@ -322,4 +396,53 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void appendRow(Sheet sheet, int rowIndex, CellStyle style, String... values) {
|
||||
Row row = sheet.createRow(rowIndex);
|
||||
for (int index = 0; index < values.length; index++) {
|
||||
row.createCell(index).setCellValue(values[index]);
|
||||
if (style != null) {
|
||||
row.getCell(index).setCellStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildEventFilterText(GaoRegistrationEventRecordRangeReq req) {
|
||||
if ((req.getRegistrationEventId() == null || req.getRegistrationEventId().isBlank()) && (req.getRegistrationEventIdList() == null || req.getRegistrationEventIdList().isEmpty())) {
|
||||
return "全部事件";
|
||||
}
|
||||
if (req.getRegistrationEventId() != null && !req.getRegistrationEventId().isBlank()) {
|
||||
return "事件 ID:" + req.getRegistrationEventId();
|
||||
}
|
||||
StringJoiner joiner = new StringJoiner("、");
|
||||
req.getRegistrationEventIdList().stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(item -> !item.isBlank())
|
||||
.forEach(joiner::add);
|
||||
return "事件 ID:" + joiner;
|
||||
}
|
||||
|
||||
private String formatExportTime(Long value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return EXPORT_TIME_FORMATTER.format(Instant.ofEpochMilli(value));
|
||||
}
|
||||
|
||||
private String getOperatorName(GaoRegistrationEventRecord record) {
|
||||
if (record.getOperator() == null) {
|
||||
return text(record.getOperatorUserId());
|
||||
}
|
||||
if (record.getOperator().getNick() != null && !record.getOperator().getNick().isBlank()) {
|
||||
return record.getOperator().getNick();
|
||||
}
|
||||
if (record.getOperator().getName() != null && !record.getOperator().getName().isBlank()) {
|
||||
return record.getOperator().getName();
|
||||
}
|
||||
return text(record.getOperatorUserId());
|
||||
}
|
||||
|
||||
private String text(Object value) {
|
||||
return value == null ? "" : value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
///
|
||||
/// 登记事件记录范围查询请求
|
||||
///
|
||||
@@ -16,6 +18,12 @@ public class GaoRegistrationEventRecordRangeReq {
|
||||
/// 登记事件 ID,为空查询全部登记事件
|
||||
private String registrationEventId;
|
||||
|
||||
/// 登记事件 ID 列表,为空查询全部登记事件
|
||||
private List<String> registrationEventIdList;
|
||||
|
||||
/// 关键词,匹配客户、登记事件、备注、登记人
|
||||
private String keyword;
|
||||
|
||||
/// 开始登记时间
|
||||
private Long beginRegisteredAt;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS `gao_customer` (
|
||||
`id` VARCHAR(36) NOT NULL,
|
||||
`code` VARCHAR(64) NOT NULL COMMENT '客户编码',
|
||||
`code` VARCHAR(64) NULL COMMENT '客户编码',
|
||||
`name` VARCHAR(64) NOT NULL COMMENT '姓名',
|
||||
`gender` VARCHAR(16) NULL COMMENT '性别代码,MALE 男,FEMALE 女',
|
||||
`age` INT NULL COMMENT '年龄',
|
||||
|
||||
@@ -1,29 +1,51 @@
|
||||
<?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.imyeyu.api.modules.gao.mapper.GaoCustomerMapper">
|
||||
<sql id="searchWhere">
|
||||
1 = 1
|
||||
AND `deleted_at` IS NULL
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (
|
||||
`code` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `name` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `telephone` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `address` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `disease` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR CAST(`conditioning_part` AS CHAR) LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="introducerCustomerId != null and introducerCustomerId != ''">
|
||||
AND `introducer_customer_id` = #{introducerCustomerId}
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<select id="search" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomer">
|
||||
SELECT
|
||||
*
|
||||
FROM `gao_customer`
|
||||
WHERE
|
||||
1 = 1
|
||||
AND `deleted_at` IS NULL
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (
|
||||
`code` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `name` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `telephone` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `address` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR `disease` LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR CAST(`conditioning_part` AS CHAR) LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="introducerCustomerId != null and introducerCustomerId != ''">
|
||||
AND `introducer_customer_id` = #{introducerCustomerId}
|
||||
</if>
|
||||
<include refid="searchWhere"/>
|
||||
ORDER BY `created_at` DESC
|
||||
</select>
|
||||
|
||||
<select id="countSearch" resultType="java.lang.Long">
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM `gao_customer`
|
||||
WHERE
|
||||
<include refid="searchWhere"/>
|
||||
</select>
|
||||
|
||||
<select id="searchPage" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomer">
|
||||
SELECT
|
||||
*
|
||||
FROM `gao_customer`
|
||||
WHERE
|
||||
<include refid="searchWhere"/>
|
||||
ORDER BY `created_at` DESC
|
||||
LIMIT #{offset}, #{size}
|
||||
</select>
|
||||
|
||||
<select id="selectInvitedList" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomer">
|
||||
SELECT
|
||||
*
|
||||
|
||||
@@ -214,20 +214,66 @@
|
||||
ORDER BY r.`registered_at` DESC, r.`created_at` DESC
|
||||
</select>
|
||||
|
||||
<sql id="rangeWhere">
|
||||
r.`registered_at` >= #{req.beginRegisteredAt}
|
||||
AND r.`registered_at` <= #{req.endRegisteredAt}
|
||||
AND r.`deleted_at` IS NULL
|
||||
<if test="req.customerId != null and req.customerId != ''">
|
||||
AND r.`customer_id` = #{req.customerId}
|
||||
</if>
|
||||
<if test="req.registrationEventId != null and req.registrationEventId != ''">
|
||||
AND r.`registration_event_id` = #{req.registrationEventId}
|
||||
</if>
|
||||
<if test="req.registrationEventIdList != null and req.registrationEventIdList.size() > 0">
|
||||
AND r.`registration_event_id` IN
|
||||
<foreach collection="req.registrationEventIdList" item="registrationEventId" open="(" separator="," close=")">
|
||||
#{registrationEventId}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="req.keyword != null and req.keyword != ''">
|
||||
AND (
|
||||
c.`code` LIKE CONCAT('%', #{req.keyword}, '%')
|
||||
OR c.`name` LIKE CONCAT('%', #{req.keyword}, '%')
|
||||
OR e.`name` LIKE CONCAT('%', #{req.keyword}, '%')
|
||||
OR r.`remark` LIKE CONCAT('%', #{req.keyword}, '%')
|
||||
OR u.`name` LIKE CONCAT('%', #{req.keyword}, '%')
|
||||
OR u.`nick` LIKE CONCAT('%', #{req.keyword}, '%')
|
||||
OR r.`operator_user_id` LIKE CONCAT('%', #{req.keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<sql id="rangeFrom">
|
||||
FROM `gao_registration_event_record` r
|
||||
LEFT JOIN `gao_customer` c ON r.`customer_id` = c.`id`
|
||||
LEFT JOIN `gao_registration_event` e ON r.`registration_event_id` = e.`id`
|
||||
LEFT JOIN `user` u ON r.`operator_user_id` = u.`id`
|
||||
</sql>
|
||||
|
||||
<select id="selectByRange" resultType="com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord">
|
||||
SELECT
|
||||
*
|
||||
FROM `gao_registration_event_record`
|
||||
r.*
|
||||
<include refid="rangeFrom"/>
|
||||
WHERE
|
||||
`registered_at` >= #{req.beginRegisteredAt}
|
||||
AND `registered_at` <= #{req.endRegisteredAt}
|
||||
AND `deleted_at` IS NULL
|
||||
<if test="req.customerId != null and req.customerId != ''">
|
||||
AND `customer_id` = #{req.customerId}
|
||||
</if>
|
||||
<if test="req.registrationEventId != null and req.registrationEventId != ''">
|
||||
AND `registration_event_id` = #{req.registrationEventId}
|
||||
</if>
|
||||
ORDER BY `registered_at` DESC, `created_at` DESC
|
||||
<include refid="rangeWhere"/>
|
||||
ORDER BY r.`registered_at` DESC, r.`created_at` DESC
|
||||
</select>
|
||||
|
||||
<select id="countByRange" resultType="java.lang.Long">
|
||||
SELECT
|
||||
COUNT(1)
|
||||
<include refid="rangeFrom"/>
|
||||
WHERE
|
||||
<include refid="rangeWhere"/>
|
||||
</select>
|
||||
|
||||
<select id="selectPageByRange" resultType="com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord">
|
||||
SELECT
|
||||
r.*
|
||||
<include refid="rangeFrom"/>
|
||||
WHERE
|
||||
<include refid="rangeWhere"/>
|
||||
ORDER BY r.`registered_at` DESC, r.`created_at` DESC
|
||||
LIMIT #{offset}, #{size}
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user