Compare commits

..
10 Commits
Author SHA1 Message Date
timi da288338cd Merge pull request 'v1.0.7' (#12) from dev into master
Reviewed-on: #12
2026-08-01 09:34:18 +00:00
Timi 2f366fe219 v1.0.7
CI / build-deploy (pull_request) Successful in 20s
CI / notify-on-failure (pull_request) Has been skipped
2026-08-01 17:34:06 +08:00
timi ea430f2468 Merge pull request 'v1.0.6' (#11) from dev into master
Reviewed-on: #11
2026-08-01 09:03:48 +00:00
Timi f1715a3e32 v1.0.6
CI / build-deploy (pull_request) Successful in 21s
CI / notify-on-failure (pull_request) Has been skipped
2026-08-01 17:03:28 +08:00
timi f76f79744a Merge pull request 'v1.0.5' (#10) from dev into master
Reviewed-on: #10
2026-08-01 08:52:52 +00:00
Timi 833a303bb6 v1.0.5
CI / build-deploy (pull_request) Successful in 21s
CI / notify-on-failure (pull_request) Has been skipped
2026-08-01 16:51:53 +08:00
timi 19daaff907 Merge pull request 'v1.0.4' (#9) from dev into master
Reviewed-on: #9
2026-08-01 03:13:00 +00:00
Timi 1658be7d8d v1.0.4
CI / build-deploy (pull_request) Successful in 21s
CI / notify-on-failure (pull_request) Has been skipped
2026-08-01 11:11:53 +08:00
timi db94d8f932 Merge pull request 'v1.0.3' (#8) from dev into master
Reviewed-on: #8
2026-07-31 17:19:48 +00:00
Timi 6c537852a0 v1.0.3
CI / build-deploy (pull_request) Successful in 1m8s
CI / notify-on-failure (pull_request) Has been skipped
2026-08-01 01:19:21 +08:00
31 changed files with 784 additions and 71 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
outputs: outputs:
deployment_status: ${{ steps.set_status.outputs.status }} deployment_status: ${{ steps.set_status.outputs.status }}
env: env:
JAVA_HOME: /usr/lib/jvm/java-21-openjdk JAVA_HOME: /usr/lib/jvm/java-25-openjdk
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
+1 -1
View File
@@ -11,7 +11,7 @@
<groupId>com.imyeyu.timiserverapi</groupId> <groupId>com.imyeyu.timiserverapi</groupId>
<artifactId>TimiServerAPI</artifactId> <artifactId>TimiServerAPI</artifactId>
<version>1.0.3</version> <version>1.0.6</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>TimiServerAPI</name> <name>TimiServerAPI</name>
<description>imyeyu.com API</description> <description>imyeyu.com API</description>
@@ -4,6 +4,7 @@ import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.mapper.RawMapper; import com.imyeyu.spring.mapper.RawMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
import java.util.List; import java.util.List;
@@ -31,6 +32,8 @@ public interface AttachmentMapper extends BaseMapper<Attachment, String>, RawMap
List<Attachment> selectByBizId(Attachment.BizType bizType, String bizId, List<String> attachTypes, Page<Attachment> page); List<Attachment> selectByBizId(Attachment.BizType bizType, String bizId, List<String> attachTypes, Page<Attachment> page);
List<Attachment> selectByBizIdList(@Param("bizType") Attachment.BizType bizType, @Param("bizIdList") List<String> bizIdList, @Param("attachTypes") List<String> attachTypes);
long countByBizId(Attachment.BizType bizType, String bizId, List<String> attachTypes); long countByBizId(Attachment.BizType bizType, String bizId, List<String> attachTypes);
/** /**
@@ -9,6 +9,7 @@ import com.imyeyu.spring.service.BaseService;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 附件服务 * 附件服务
@@ -50,6 +51,16 @@ public interface AttachmentService extends BaseService<Attachment, String> {
*/ */
List<Attachment> listByBizId(Attachment.BizType bizType, String bizId, String... attachTypes); List<Attachment> listByBizId(Attachment.BizType bizType, String bizId, String... attachTypes);
/**
* 根据多个业务 ID 批量获取附件。
*
* @param bizType 业务类型
* @param bizIdList 业务 ID 列表
* @param attachTypes 附件类型,可为 null
* @return 按业务 ID 分组的附件列表
*/
Map<String, List<Attachment>> mapByBizIdList(Attachment.BizType bizType, List<String> bizIdList, String... attachTypes);
/** /**
* 根据业务获取所有附件 * 根据业务获取所有附件
* *
@@ -43,7 +43,9 @@ import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@@ -310,6 +312,22 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
return mapper.selectByBizId(bizType, bizId, List.of(attachTypes), null); return mapper.selectByBizId(bizType, bizId, List.of(attachTypes), null);
} }
@Override
public Map<String, List<Attachment>> mapByBizIdList(Attachment.BizType bizType, List<String> bizIdList, String... attachTypes) {
if (bizIdList == null || bizIdList.isEmpty()) {
return Map.of();
}
List<String> normalizedBizIdList = bizIdList.stream()
.filter(TimiJava::isNotEmpty)
.distinct()
.toList();
if (normalizedBizIdList.isEmpty()) {
return Map.of();
}
return mapper.selectByBizIdList(bizType, new ArrayList<>(normalizedBizIdList), List.of(attachTypes)).stream()
.collect(Collectors.groupingBy(Attachment::getBizId));
}
@Override @Override
public long countByBizId(Attachment.BizType bizType, String bizId, String... attachTypes) { public long countByBizId(Attachment.BizType bizType, String bizId, String... attachTypes) {
return mapper.countByBizId(bizType, bizId, List.of(attachTypes)); return mapper.countByBizId(bizType, bizId, List.of(attachTypes));
@@ -0,0 +1,29 @@
package com.imyeyu.api.modules.forevermc.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 服务器接口
*
* @author 夜雨
* @since 2024-08-06 17:20
*/
@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/fmc/server")
public class ServerController {
/**
* 服务器状态报告,由 FMCServer 服务端插件发送
*/
@PostMapping("/report")
public void report() {
}
}
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.controller; package com.imyeyu.api.modules.gao.controller;
import com.fasterxml.jackson.annotation.JsonView;
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
import com.imyeyu.api.modules.gao.bean.GaoRoleCode; import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
import com.imyeyu.api.bean.RoleMatchMode; import com.imyeyu.api.bean.RoleMatchMode;
@@ -12,6 +13,7 @@ import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService;
import com.imyeyu.api.modules.gao.service.GaoCustomerService; import com.imyeyu.api.modules.gao.service.GaoCustomerService;
import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService; import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService;
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq;
import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq;
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq;
@@ -21,6 +23,7 @@ import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.spring.util.ResponseView;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -57,6 +60,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/list") @PostMapping("/list")
@JsonView(ResponseView.Public.class)
public PageResult<GaoCustomer> list(@RequestBody Page<GaoCustomer> page) { public PageResult<GaoCustomer> list(@RequestBody Page<GaoCustomer> page) {
PageResult<GaoCustomer> result = service.page(page); PageResult<GaoCustomer> result = service.page(page);
fillLastSignInAt(result.getList()); fillLastSignInAt(result.getList());
@@ -67,6 +71,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/search") @PostMapping("/search")
@JsonView(ResponseView.Public.class)
public List<GaoCustomer> search(@RequestBody GaoCustomerSearchReq req) { public List<GaoCustomer> search(@RequestBody GaoCustomerSearchReq req) {
return service.search(req); return service.search(req);
} }
@@ -75,6 +80,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/search/page") @PostMapping("/search/page")
@JsonView(ResponseView.Public.class)
public PageResult<GaoCustomer> searchPage(@RequestBody Page<GaoCustomerSearchReq> page) { public PageResult<GaoCustomer> searchPage(@RequestBody Page<GaoCustomerSearchReq> page) {
return service.searchPage(page); return service.searchPage(page);
} }
@@ -95,6 +101,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/detail") @PostMapping("/detail")
@JsonView(ResponseView.Public.class)
public GaoCustomer detail(@RequestParam String id) { public GaoCustomer detail(@RequestParam String id) {
return service.get(id); return service.get(id);
} }
@@ -103,6 +110,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/detail/code") @PostMapping("/detail/code")
@JsonView(ResponseView.Public.class)
public GaoCustomer detailByCode(@RequestParam String code) { public GaoCustomer detailByCode(@RequestParam String code) {
return service.getByCode(code); return service.getByCode(code);
} }
@@ -111,6 +119,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/detail/code/nullable") @PostMapping("/detail/code/nullable")
@JsonView(ResponseView.Public.class)
public GaoCustomer nullableDetailByCode(@RequestParam String code) { public GaoCustomer nullableDetailByCode(@RequestParam String code) {
return service.getNullableByCode(code); return service.getNullableByCode(code);
} }
@@ -119,6 +128,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/invited/list") @PostMapping("/invited/list")
@JsonView(ResponseView.Public.class)
public List<GaoCustomer> invitedList(@RequestParam String introducerCustomerId) { public List<GaoCustomer> invitedList(@RequestParam String introducerCustomerId) {
return service.listInvited(introducerCustomerId); return service.listInvited(introducerCustomerId);
} }
@@ -127,6 +137,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_CREATE) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_CREATE)
@PostMapping("/create") @PostMapping("/create")
@JsonView(ResponseView.Public.class)
public GaoCustomer create(@RequestBody GaoCustomer customer) { public GaoCustomer create(@RequestBody GaoCustomer customer) {
service.create(customer); service.create(customer);
return customer; return customer;
@@ -135,6 +146,7 @@ public class GaoCustomerController {
@AOPLog @AOPLog
@RequestRateLimit @RequestRateLimit
@PostMapping("/quick/register") @PostMapping("/quick/register")
@JsonView(ResponseView.Public.class)
public GaoRegistrationEventRecord quickRegister(@RequestBody GaoQuickRegistrationEventRecordReq req) { public GaoRegistrationEventRecord quickRegister(@RequestBody GaoQuickRegistrationEventRecordReq req) {
return registrationEventRecordService.quickRegister(req); return registrationEventRecordService.quickRegister(req);
} }
@@ -167,6 +179,7 @@ public class GaoCustomerController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/invite/detail") @PostMapping("/invite/detail")
@JsonView(ResponseView.Public.class)
public GaoCustomer inviteDetail(@RequestParam String customerId) { public GaoCustomer inviteDetail(@RequestParam String customerId) {
return service.get(customerId); return service.get(customerId);
} }
@@ -196,6 +209,15 @@ public class GaoCustomerController {
return service.statDailyTrend(req); return service.statDailyTrend(req);
} }
@AOPLog
@RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
@PostMapping("/stat/gender")
/// 统计客户性别分布
public List<GaoCustomerGenderStatView> genderStat() {
return service.statGender();
}
private void fillLastSignInAt(List<GaoCustomer> customerList) { private void fillLastSignInAt(List<GaoCustomer> customerList) {
if (customerList == null || customerList.isEmpty()) { if (customerList == null || customerList.isEmpty()) {
return; return;
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.controller; package com.imyeyu.api.modules.gao.controller;
import com.fasterxml.jackson.annotation.JsonView;
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
import com.imyeyu.api.modules.gao.bean.GaoRoleCode; import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
import com.imyeyu.api.bean.RoleMatchMode; import com.imyeyu.api.bean.RoleMatchMode;
@@ -10,7 +11,9 @@ import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq;
@@ -20,6 +23,7 @@ import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.spring.util.ResponseView;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -49,6 +53,7 @@ public class GaoRegistrationEventRecordController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
@PostMapping("/list") @PostMapping("/list")
@JsonView(ResponseView.Public.class)
public PageResult<GaoRegistrationEventRecord> list(@RequestBody Page<GaoRegistrationEventRecord> page) { public PageResult<GaoRegistrationEventRecord> list(@RequestBody Page<GaoRegistrationEventRecord> page) {
return service.page(page); return service.page(page);
} }
@@ -57,7 +62,7 @@ public class GaoRegistrationEventRecordController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
@PostMapping("/range/page") @PostMapping("/range/page")
public PageResult<GaoRegistrationEventRecord> rangePage(@RequestBody Page<GaoRegistrationEventRecordRangeReq> page) { public PageResult<GaoRegistrationEventRecordListItemView> rangePage(@RequestBody Page<GaoRegistrationEventRecordRangeReq> page) {
return service.pageByRange(page); return service.pageByRange(page);
} }
@@ -65,6 +70,7 @@ public class GaoRegistrationEventRecordController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
@PostMapping("/detail") @PostMapping("/detail")
@JsonView(ResponseView.Public.class)
public GaoRegistrationEventRecord detail(@RequestParam String id) { public GaoRegistrationEventRecord detail(@RequestParam String id) {
return service.get(id); return service.get(id);
} }
@@ -72,6 +78,7 @@ public class GaoRegistrationEventRecordController {
@AOPLog @AOPLog
@RequestRateLimit @RequestRateLimit
@PostMapping("/create") @PostMapping("/create")
@JsonView(ResponseView.Public.class)
public GaoRegistrationEventRecord create(@RequestBody GaoRegistrationEventRecordCreateReq req) { public GaoRegistrationEventRecord create(@RequestBody GaoRegistrationEventRecordCreateReq req) {
return service.createRecord(req); return service.createRecord(req);
} }
@@ -96,6 +103,7 @@ public class GaoRegistrationEventRecordController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
@PostMapping("/period/list") @PostMapping("/period/list")
@JsonView(ResponseView.Public.class)
public List<GaoRegistrationEventRecord> periodList(@RequestBody GaoRegistrationEventRecordPeriodReq req) { public List<GaoRegistrationEventRecord> periodList(@RequestBody GaoRegistrationEventRecordPeriodReq req) {
return service.listByRegistrationEventAndPeriod(req); return service.listByRegistrationEventAndPeriod(req);
} }
@@ -104,10 +112,19 @@ public class GaoRegistrationEventRecordController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
@PostMapping("/range/list") @PostMapping("/range/list")
@JsonView(ResponseView.Public.class)
public List<GaoRegistrationEventRecord> rangeList(@RequestBody GaoRegistrationEventRecordRangeReq req) { public List<GaoRegistrationEventRecord> rangeList(@RequestBody GaoRegistrationEventRecordRangeReq req) {
return service.listByRange(req); return service.listByRange(req);
} }
@AOPLog
@RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ)
@PostMapping("/range/calendar/list")
public List<GaoRegistrationEventRecordCalendarView> calendarRangeList(@RequestBody GaoRegistrationEventRecordRangeReq req) {
return service.listCalendarByRange(req);
}
@AOPLog @AOPLog
@RequestRateLimit @RequestRateLimit
@IgnoreGlobalReturn @IgnoreGlobalReturn
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.controller; package com.imyeyu.api.modules.gao.controller;
import com.fasterxml.jackson.annotation.JsonView;
import com.imyeyu.api.bean.ModuleCode; import com.imyeyu.api.bean.ModuleCode;
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission; import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole; import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
@@ -13,6 +14,7 @@ import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.spring.util.ResponseView;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@@ -40,6 +42,7 @@ public class GaoUserController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.USER_READ) @RequireGaoPermission(GaoPermissionCode.USER_READ)
@PostMapping("/list") @PostMapping("/list")
@JsonView(ResponseView.Public.class)
public PageResult<GaoUser> list(@RequestBody Page<GaoUser> page) { public PageResult<GaoUser> list(@RequestBody Page<GaoUser> page) {
return service.pageWithUser(page); return service.pageWithUser(page);
} }
@@ -48,6 +51,7 @@ public class GaoUserController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.USER_READ) @RequireGaoPermission(GaoPermissionCode.USER_READ)
@PostMapping("/detail") @PostMapping("/detail")
@JsonView(ResponseView.Public.class)
public GaoUser detail(@RequestParam String id) { public GaoUser detail(@RequestParam String id) {
return service.get(id); return service.get(id);
} }
@@ -56,6 +60,7 @@ public class GaoUserController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.USER_READ) @RequireGaoPermission(GaoPermissionCode.USER_READ)
@PostMapping("/detail/user") @PostMapping("/detail/user")
@JsonView(ResponseView.Public.class)
public GaoUser detailByUserId(@RequestParam String userId) { public GaoUser detailByUserId(@RequestParam String userId) {
return service.getByUserId(userId); return service.getByUserId(userId);
} }
@@ -64,6 +69,7 @@ public class GaoUserController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.USER_CREATE) @RequireGaoPermission(GaoPermissionCode.USER_CREATE)
@PostMapping("/create") @PostMapping("/create")
@JsonView(ResponseView.Public.class)
public GaoUser create(@RequestBody GaoUser gaoUser) { public GaoUser create(@RequestBody GaoUser gaoUser) {
service.create(gaoUser); service.create(gaoUser);
return service.get(gaoUser.getId()); return service.get(gaoUser.getId());
@@ -73,6 +79,7 @@ public class GaoUserController {
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.USER_UPDATE) @RequireGaoPermission(GaoPermissionCode.USER_UPDATE)
@PostMapping("/update") @PostMapping("/update")
@JsonView(ResponseView.Public.class)
public GaoUser update(@RequestBody GaoUser gaoUser) { public GaoUser update(@RequestBody GaoUser gaoUser) {
service.update(gaoUser); service.update(gaoUser);
return service.get(gaoUser.getId()); return service.get(gaoUser.getId());
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.entity;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.user.entity.User;
import com.imyeyu.api.modules.user.bean.Gender; import com.imyeyu.api.modules.user.bean.Gender;
import com.imyeyu.spring.annotation.table.Transient; import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.UUIDEntity; import com.imyeyu.spring.entity.UUIDEntity;
@@ -59,6 +60,9 @@ public class GaoCustomer extends UUIDEntity {
/// 介绍人客户 ID /// 介绍人客户 ID
private String introducerCustomerId; private String introducerCustomerId;
/// 录入用户 ID
private String creatorUserId;
@Transient @Transient
private Long lastSignInAt; private Long lastSignInAt;
@@ -66,6 +70,8 @@ public class GaoCustomer extends UUIDEntity {
private GaoCustomer introducerCustomer; private GaoCustomer introducerCustomer;
@Transient @Transient
private User creatorUser;
private Long invitedCount; private Long invitedCount;
@Transient @Transient
@@ -16,18 +16,73 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class GaoRegistrationEvent extends UUIDEntity { public class GaoRegistrationEvent extends UUIDEntity {
///
/// 登记事件状态
///
/// @author Codex
/// @since 2026-08-01
public enum Status {
/// 可登记
ACTIVE,
/// 无效,不可登记但保留统计
INACTIVE,
/// 已删除,不可登记但保留统计
DELETED
}
///
/// 登记限制类型
///
/// @author Codex
/// @since 2026-08-01
public enum RegistrationLimitType {
/// 不限制
NONE,
/// 每天上午、下午各一次
DAILY_HALF_DAY,
/// 每个自然日一次
NATURAL_DAY,
/// 每隔 N 小时一次
INTERVAL_HOUR,
/// 每个自然月一次
NATURAL_MONTH,
/// 累计限 N 次
TOTAL_LIMIT
}
/// 登记事件名称 /// 登记事件名称
private String name; private String name;
/// 说明 /// 说明
private String description; private String description;
/// 统计图表颜色
private String color;
/// 状态
private Status status;
/// true 为启用 /// true 为启用
private Boolean enabled; private Boolean enabled;
/// true 为登记时要求拍照 /// true 为登记时要求拍照
private Boolean requirePhoto; private Boolean requirePhoto;
/// 登记限制类型
private RegistrationLimitType limitType;
/// 登记限制数值,按限制类型表示小时数或次数
private Integer limitValue;
/// 排序值 /// 排序值
private Integer sort; private Integer sort;
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.mapper;
import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.entity.GaoCustomer;
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
@@ -21,6 +22,8 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
@Select("SELECT * FROM `gao_customer` WHERE `name` = #{name} AND `deleted_at` IS NULL LIMIT 1") @Select("SELECT * FROM `gao_customer` WHERE `name` = #{name} AND `deleted_at` IS NULL LIMIT 1")
GaoCustomer selectByName(@Param("name") String name); GaoCustomer selectByName(@Param("name") String name);
List<GaoCustomer> selectByIdList(@Param("idList") List<String> idList);
List<GaoCustomer> search(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId); List<GaoCustomer> search(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId);
Long countSearch(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId); Long countSearch(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId);
@@ -31,6 +34,8 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
Long countInvited(@Param("introducerCustomerId") String introducerCustomerId); Long countInvited(@Param("introducerCustomerId") String introducerCustomerId);
void updateInvitedCountDelta(@Param("customerId") String customerId, @Param("delta") Long delta);
/// 统计每日新增客户数 /// 统计每日新增客户数
/// ///
/// @param beginCreatedAt 开始创建时间 /// @param beginCreatedAt 开始创建时间
@@ -43,4 +48,9 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
/// @param beginCreatedAt 开始创建时间 /// @param beginCreatedAt 开始创建时间
/// @return 客户总数 /// @return 客户总数
Long countCreatedBefore(@Param("beginCreatedAt") Long beginCreatedAt); Long countCreatedBefore(@Param("beginCreatedAt") Long beginCreatedAt);
/// 统计客户性别分布
///
/// @return 性别统计列表
List<GaoCustomerGenderStatView> statGender();
} }
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.mapper;
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
import java.util.List; import java.util.List;
@@ -13,6 +14,8 @@ import java.util.List;
/// @since 2026-07-27 /// @since 2026-07-27
public interface GaoRegistrationEventMapper extends BaseMapper<GaoRegistrationEvent, String> { public interface GaoRegistrationEventMapper extends BaseMapper<GaoRegistrationEvent, String> {
@Select("SELECT * FROM `gao_registration_event` WHERE `enabled` = TRUE AND `deleted_at` IS NULL ORDER BY `sort` ASC, `created_at` ASC") List<GaoRegistrationEvent> selectByIdList(@Param("idList") List<String> idList);
@Select("SELECT * FROM `gao_registration_event` WHERE `enabled` = TRUE AND (`status` IS NULL OR `status` = 'ACTIVE') AND `deleted_at` IS NULL ORDER BY `sort` ASC, `created_at` ASC")
List<GaoRegistrationEvent> selectEnabledList(); List<GaoRegistrationEvent> selectEnabledList();
} }
@@ -3,7 +3,9 @@ package com.imyeyu.api.modules.gao.mapper;
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
@@ -25,6 +27,22 @@ public interface GaoRegistrationEventRecordMapper extends BaseMapper<GaoRegistra
/// @return 最后登记事件记录列表 /// @return 最后登记事件记录列表
List<GaoRegistrationEventRecord> selectByRegistrationEventAndPeriod(@Param("registrationEventId") String registrationEventId, @Param("periodType") GaoRegistrationEventRecordPeriodReq.PeriodType periodType, @Param("periodValue") Integer periodValue); List<GaoRegistrationEventRecord> selectByRegistrationEventAndPeriod(@Param("registrationEventId") String registrationEventId, @Param("periodType") GaoRegistrationEventRecordPeriodReq.PeriodType periodType, @Param("periodValue") Integer periodValue);
/// 统计客户指定登记事件在时间范围内的登记数
///
/// @param customerId 客户 ID
/// @param registrationEventId 登记事件 ID
/// @param beginRegisteredAt 开始登记时间
/// @param endRegisteredAt 结束登记时间
/// @return 登记数
Long countByCustomerIdAndRegistrationEventIdRange(@Param("customerId") String customerId, @Param("registrationEventId") String registrationEventId, @Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt);
/// 统计客户指定登记事件总登记数
///
/// @param customerId 客户 ID
/// @param registrationEventId 登记事件 ID
/// @return 登记数
Long countByCustomerIdAndRegistrationEventId(@Param("customerId") String customerId, @Param("registrationEventId") String registrationEventId);
/// 按周期统计登记事件记录 /// 按周期统计登记事件记录
/// ///
/// @param registrationEventId 登记事件 ID /// @param registrationEventId 登记事件 ID
@@ -104,6 +122,12 @@ public interface GaoRegistrationEventRecordMapper extends BaseMapper<GaoRegistra
/// @return 登记事件记录列表 /// @return 登记事件记录列表
List<GaoRegistrationEventRecord> selectByRange(@Param("req") GaoRegistrationEventRecordRangeReq req); List<GaoRegistrationEventRecord> selectByRange(@Param("req") GaoRegistrationEventRecordRangeReq req);
/// 按任意时间范围查询日历轻量记录
///
/// @param req 范围查询请求
/// @return 日历轻量记录列表
List<GaoRegistrationEventRecordCalendarView> selectCalendarByRange(@Param("req") GaoRegistrationEventRecordRangeReq req);
/// 按任意时间范围统计登记事件记录 /// 按任意时间范围统计登记事件记录
/// ///
/// @param req 范围查询请求 /// @param req 范围查询请求
@@ -116,5 +140,5 @@ public interface GaoRegistrationEventRecordMapper extends BaseMapper<GaoRegistra
/// @param offset 偏移量 /// @param offset 偏移量
/// @param size 每页条数 /// @param size 每页条数
/// @return 登记事件记录列表 /// @return 登记事件记录列表
List<GaoRegistrationEventRecord> selectPageByRange(@Param("req") GaoRegistrationEventRecordRangeReq req, @Param("offset") Long offset, @Param("size") Long size); List<GaoRegistrationEventRecordListItemView> selectPageByRange(@Param("req") GaoRegistrationEventRecordRangeReq req, @Param("offset") Long offset, @Param("size") Long size);
} }
@@ -18,6 +18,19 @@ public interface GaoRegistrationEventRoleRelationMapper extends BaseMapper<GaoRe
@Select("SELECT `role_code` FROM `gao_registration_event_role_relation` WHERE `registration_event_id` = #{registrationEventId} AND `deleted_at` IS NULL ORDER BY `created_at` ASC") @Select("SELECT `role_code` FROM `gao_registration_event_role_relation` WHERE `registration_event_id` = #{registrationEventId} AND `deleted_at` IS NULL ORDER BY `created_at` ASC")
List<String> selectRoleCodeListByRegistrationEventId(@Param("registrationEventId") String registrationEventId); List<String> selectRoleCodeListByRegistrationEventId(@Param("registrationEventId") String registrationEventId);
@Select({
"<script>",
"SELECT * FROM `gao_registration_event_role_relation`",
"WHERE `registration_event_id` IN",
"<foreach collection='registrationEventIdList' item='registrationEventId' open='(' separator=',' close=')'>",
"#{registrationEventId}",
"</foreach>",
"AND `deleted_at` IS NULL",
"ORDER BY `created_at` ASC",
"</script>"
})
List<GaoRegistrationEventRoleRelation> selectByRegistrationEventIdList(@Param("registrationEventIdList") List<String> registrationEventIdList);
@Update("UPDATE `gao_registration_event_role_relation` SET `deleted_at` = UNIX_TIMESTAMP() WHERE `registration_event_id` = #{registrationEventId} AND `deleted_at` IS NULL") @Update("UPDATE `gao_registration_event_role_relation` SET `deleted_at` = UNIX_TIMESTAMP() WHERE `registration_event_id` = #{registrationEventId} AND `deleted_at` IS NULL")
void deleteByRegistrationEventId(@Param("registrationEventId") String registrationEventId); void deleteByRegistrationEventId(@Param("registrationEventId") String registrationEventId);
} }
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.service;
import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.entity.GaoCustomer;
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq;
import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq;
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq;
@@ -76,4 +77,9 @@ public interface GaoCustomerService extends BaseService<GaoCustomer, String> {
/// @param req 趋势统计请求 /// @param req 趋势统计请求
/// @return 每日趋势列表 /// @return 每日趋势列表
List<GaoCustomerDailyStatView> statDailyTrend(GaoCustomerTrendStatReq req); List<GaoCustomerDailyStatView> statDailyTrend(GaoCustomerTrendStatReq req);
/// 统计客户性别分布
///
/// @return 性别统计列表
List<GaoCustomerGenderStatView> statGender();
} }
@@ -4,8 +4,10 @@ import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq; import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq;
@@ -42,11 +44,17 @@ public interface GaoRegistrationEventRecordService extends BaseService<GaoRegist
/// @return 登记事件记录列表 /// @return 登记事件记录列表
List<GaoRegistrationEventRecord> listByRange(GaoRegistrationEventRecordRangeReq req); List<GaoRegistrationEventRecord> listByRange(GaoRegistrationEventRecordRangeReq req);
/// 按任意时间范围查询日历轻量记录
///
/// @param req 范围查询请求
/// @return 日历轻量记录列表
List<GaoRegistrationEventRecordCalendarView> listCalendarByRange(GaoRegistrationEventRecordRangeReq req);
/// 按任意时间范围分页查询登记事件记录 /// 按任意时间范围分页查询登记事件记录
/// ///
/// @param page 分页参数 /// @param page 分页参数
/// @return 登记事件记录分页结果 /// @return 登记事件记录分页结果
PageResult<GaoRegistrationEventRecord> pageByRange(Page<GaoRegistrationEventRecordRangeReq> page); PageResult<GaoRegistrationEventRecordListItemView> pageByRange(Page<GaoRegistrationEventRecordRangeReq> page);
/// 按任意时间范围导出登记事件记录 Excel /// 按任意时间范围导出登记事件记录 Excel
/// ///
@@ -7,7 +7,10 @@ import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq;
import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.entity.GaoCustomer;
import com.imyeyu.api.modules.gao.mapper.GaoCustomerMapper; import com.imyeyu.api.modules.gao.mapper.GaoCustomerMapper;
import com.imyeyu.api.modules.gao.service.GaoCustomerService; import com.imyeyu.api.modules.gao.service.GaoCustomerService;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.api.modules.user.service.UserService;
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq;
import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq;
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq;
@@ -39,6 +42,8 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/// ///
/// 客户服务实现 /// 客户服务实现
@@ -54,6 +59,8 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
private final GaoCustomerMapper mapper; private final GaoCustomerMapper mapper;
private final AttachmentService attachmentService; private final AttachmentService attachmentService;
private final UserLoginService userLoginService;
private final UserService userService;
@Override @Override
protected BaseMapper<GaoCustomer, String> mapper() { protected BaseMapper<GaoCustomer, String> mapper() {
@@ -63,22 +70,34 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
@Override @Override
public void create(GaoCustomer entity) { public void create(GaoCustomer entity) {
checkEntity(entity, false); checkEntity(entity, false);
entity.setCreatorUserId(userLoginService.getRequireLoginUserId());
entity.setInvitedCount(0L);
super.create(entity); super.create(entity);
saveAttachment(entity.getId(), resolveAttachmentList(entity)); saveAttachment(entity.getId(), resolveAttachmentList(entity));
loadTransient(entity); changeInvitedCount(entity.getIntroducerCustomerId(), 1L);
loadDetailTransient(entity);
} }
@Override @Override
public void update(GaoCustomer entity) { public void update(GaoCustomer entity) {
checkEntity(entity, true); GaoCustomer dbCustomer = checkEntity(entity, true);
super.update(entity); super.update(entity);
saveAttachment(entity.getId(), resolveAttachmentList(entity)); saveAttachment(entity.getId(), resolveAttachmentList(entity));
syncIntroducerInvitedCount(dbCustomer.getIntroducerCustomerId(), entity.getIntroducerCustomerId());
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void delete(String id) {
GaoCustomer customer = super.get(id);
super.delete(id);
changeInvitedCount(customer.getIntroducerCustomerId(), -1L);
} }
@Override @Override
public GaoCustomer get(String id) { public GaoCustomer get(String id) {
GaoCustomer customer = super.get(id); GaoCustomer customer = super.get(id);
loadTransient(customer); loadDetailTransient(customer);
return customer; return customer;
} }
@@ -87,7 +106,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
TimiException.required(code, "not found customer.code"); TimiException.required(code, "not found customer.code");
GaoCustomer customer = mapper.selectByCode(code); GaoCustomer customer = mapper.selectByCode(code);
TimiException.required(customer, "not found customer"); TimiException.required(customer, "not found customer");
loadTransient(customer); loadDetailTransient(customer);
return customer; return customer;
} }
@@ -95,7 +114,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
public GaoCustomer getNullableByCode(String code) { public GaoCustomer getNullableByCode(String code) {
TimiException.required(code, "not found customer.code"); TimiException.required(code, "not found customer.code");
GaoCustomer customer = mapper.selectByCode(code); GaoCustomer customer = mapper.selectByCode(code);
loadTransient(customer); loadDetailTransient(customer);
return customer; return customer;
} }
@@ -117,7 +136,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
public List<GaoCustomer> search(GaoCustomerSearchReq req) { public List<GaoCustomer> search(GaoCustomerSearchReq req) {
req = resolveSearchReq(req); req = resolveSearchReq(req);
List<GaoCustomer> list = mapper.search(req.getKeyword(), req.getIntroducerCustomerId()); List<GaoCustomer> list = mapper.search(req.getKeyword(), req.getIntroducerCustomerId());
list.forEach(this::loadTransient); loadListTransient(list);
return list; return list;
} }
@@ -131,7 +150,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
GaoCustomerSearchReq req = resolveSearchReq(page.getEqualsExample()); GaoCustomerSearchReq req = resolveSearchReq(page.getEqualsExample());
Long total = TimiJava.defaultIfNull(mapper.countSearch(req.getKeyword(), req.getIntroducerCustomerId()), 0L); 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<GaoCustomer> list = mapper.searchPage(req.getKeyword(), req.getIntroducerCustomerId(), page.getIndex() * page.getSize(), page.getSize());
list.forEach(this::loadTransient); loadListTransient(list);
PageResult<GaoCustomer> result = new PageResult<>(); PageResult<GaoCustomer> result = new PageResult<>();
result.setTotal(total); result.setTotal(total);
result.setList(list); result.setList(list);
@@ -141,6 +160,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
@Override @Override
public byte[] exportSearchExcel(GaoCustomerSearchReq req) throws IOException { public byte[] exportSearchExcel(GaoCustomerSearchReq req) throws IOException {
List<GaoCustomer> list = search(req); List<GaoCustomer> list = search(req);
loadIntroducerCustomer(list);
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet("客户列表"); Sheet sheet = workbook.createSheet("客户列表");
CellStyle titleStyle = workbook.createCellStyle(); CellStyle titleStyle = workbook.createCellStyle();
@@ -188,7 +208,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
public List<GaoCustomer> listInvited(String introducerCustomerId) { public List<GaoCustomer> listInvited(String introducerCustomerId) {
TimiException.required(introducerCustomerId, "not found introducerCustomerId"); TimiException.required(introducerCustomerId, "not found introducerCustomerId");
List<GaoCustomer> list = mapper.selectInvitedList(introducerCustomerId); List<GaoCustomer> list = mapper.selectInvitedList(introducerCustomerId);
list.forEach(this::loadTransient); loadListTransient(list);
return list; return list;
} }
@@ -198,9 +218,11 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
TimiException.required(req, "not found invite req"); TimiException.required(req, "not found invite req");
TimiException.required(req.getCustomerId(), "not found invite req.customerId"); TimiException.required(req.getCustomerId(), "not found invite req.customerId");
GaoCustomer customer = super.get(req.getCustomerId()); GaoCustomer customer = super.get(req.getCustomerId());
String oldIntroducerCustomerId = customer.getIntroducerCustomerId();
customer.setIntroducerCustomerId(req.getIntroducerCustomerId()); customer.setIntroducerCustomerId(req.getIntroducerCustomerId());
checkEntity(customer, true); checkEntity(customer, true);
super.update(customer); super.update(customer);
syncIntroducerInvitedCount(oldIntroducerCustomerId, customer.getIntroducerCustomerId());
} }
@Transactional(TimiServerDBConfig.ROLLBACKER) @Transactional(TimiServerDBConfig.ROLLBACKER)
@@ -208,8 +230,10 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
public void deleteInviteRelation(String customerId) { public void deleteInviteRelation(String customerId) {
TimiException.required(customerId, "not found customerId"); TimiException.required(customerId, "not found customerId");
GaoCustomer customer = super.get(customerId); GaoCustomer customer = super.get(customerId);
String oldIntroducerCustomerId = customer.getIntroducerCustomerId();
customer.setIntroducerCustomerId(null); customer.setIntroducerCustomerId(null);
super.update(customer); super.update(customer);
changeInvitedCount(oldIntroducerCustomerId, -1L);
} }
@Override @Override
@@ -243,6 +267,11 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
return list; return list;
} }
@Override
public List<GaoCustomerGenderStatView> statGender() {
return mapper.statGender();
}
private void checkDailyStatRange(LocalDate beginDate, LocalDate endDate) { private void checkDailyStatRange(LocalDate beginDate, LocalDate endDate) {
long days = ChronoUnit.DAYS.between(beginDate, endDate) + 1; long days = ChronoUnit.DAYS.between(beginDate, endDate) + 1;
TimiException.requiredTrue(days <= 366, "daily stat range too large"); TimiException.requiredTrue(days <= 366, "daily stat range too large");
@@ -252,21 +281,26 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
return req == null ? new GaoCustomerSearchReq() : req; return req == null ? new GaoCustomerSearchReq() : req;
} }
private void checkEntity(GaoCustomer entity, boolean requireId) { private GaoCustomer checkEntity(GaoCustomer entity, boolean requireId) {
TimiException.required(entity, "not found customer"); TimiException.required(entity, "not found customer");
GaoCustomer dbCustomer = null;
entity.setCode(normalizeText(entity.getCode()));
entity.setIntroducerCustomerId(normalizeText(entity.getIntroducerCustomerId()));
if (requireId) { if (requireId) {
TimiException.required(entity.getId(), "not found customer.id"); TimiException.required(entity.getId(), "not found customer.id");
dbCustomer = super.get(entity.getId());
entity.setCreatorUserId(dbCustomer.getCreatorUserId());
} }
TimiException.required(entity.getName(), "not found customer.name"); TimiException.required(entity.getName(), "not found customer.name");
entity.setName(entity.getName().trim()); entity.setName(entity.getName().trim());
TimiException.requiredTrue(!entity.getName().isEmpty(), "not found customer.name"); TimiException.requiredTrue(!entity.getName().isEmpty(), "not found customer.name");
GaoCustomer dbCustomer = mapper.selectByName(entity.getName()); GaoCustomer duplicateCustomer = mapper.selectByName(entity.getName());
if (dbCustomer != null && !dbCustomer.getId().equals(entity.getId())) { if (duplicateCustomer != null && !duplicateCustomer.getId().equals(entity.getId())) {
throw new TimiException(TimiCode.ARG_BAD, "客户姓名已存在"); throw new TimiException(TimiCode.ARG_BAD, "客户姓名已存在");
} }
if (TimiJava.isNotEmpty(entity.getCode())) { if (TimiJava.isNotEmpty(entity.getCode())) {
dbCustomer = mapper.selectByCode(entity.getCode()); duplicateCustomer = mapper.selectByCode(entity.getCode());
if (dbCustomer != null && !dbCustomer.getId().equals(entity.getId())) { if (duplicateCustomer != null && !duplicateCustomer.getId().equals(entity.getId())) {
throw new TimiException(TimiCode.ARG_BAD, "客户编码已存在"); throw new TimiException(TimiCode.ARG_BAD, "客户编码已存在");
} }
} }
@@ -274,17 +308,82 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
TimiException.requiredTrue(!entity.getIntroducerCustomerId().equals(entity.getId()), "customer.introducerCustomerId invalid"); TimiException.requiredTrue(!entity.getIntroducerCustomerId().equals(entity.getId()), "customer.introducerCustomerId invalid");
TimiException.required(super.get(entity.getIntroducerCustomerId()), "not found introducer customer"); TimiException.required(super.get(entity.getIntroducerCustomerId()), "not found introducer customer");
} }
return dbCustomer;
} }
private void loadTransient(GaoCustomer customer) { private void loadDetailTransient(GaoCustomer customer) {
if (customer == null) { if (customer == null) {
return; return;
} }
if (TimiJava.isNotEmpty(customer.getIntroducerCustomerId())) { loadIntroducerCustomer(List.of(customer));
customer.setIntroducerCustomer(super.get(customer.getIntroducerCustomerId())); if (TimiJava.isNotEmpty(customer.getCreatorUserId())) {
customer.setCreatorUser(userService.get(customer.getCreatorUserId()));
} }
customer.setInvitedCount(mapper.countInvited(customer.getId())); loadAttachmentList(List.of(customer));
customer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, customer.getId(), GaoCustomer.AttachType.PHOTO.name())); }
private void loadListTransient(List<GaoCustomer> customerList) {
loadAttachmentList(customerList);
}
private void loadIntroducerCustomer(List<GaoCustomer> customerList) {
if (customerList == null || customerList.isEmpty()) {
return;
}
List<String> introducerCustomerIdList = customerList.stream()
.map(GaoCustomer::getIntroducerCustomerId)
.filter(TimiJava::isNotEmpty)
.distinct()
.toList();
if (introducerCustomerIdList.isEmpty()) {
return;
}
Map<String, GaoCustomer> introducerCustomerMap = mapper.selectByIdList(new ArrayList<>(introducerCustomerIdList)).stream()
.collect(Collectors.toMap(GaoCustomer::getId, item -> item, (left, right) -> left));
for (GaoCustomer customer : customerList) {
customer.setIntroducerCustomer(introducerCustomerMap.get(customer.getIntroducerCustomerId()));
}
}
private void loadAttachmentList(List<GaoCustomer> customerList) {
if (customerList == null || customerList.isEmpty()) {
return;
}
List<String> customerIdList = customerList.stream()
.map(GaoCustomer::getId)
.filter(TimiJava::isNotEmpty)
.distinct()
.toList();
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList, GaoCustomer.AttachType.PHOTO.name());
for (GaoCustomer customer : customerList) {
customer.setAttachmentList(attachmentMap.getOrDefault(customer.getId(), List.of()));
}
}
private void syncIntroducerInvitedCount(String oldIntroducerCustomerId, String newIntroducerCustomerId) {
String normalizedOldIntroducerCustomerId = normalizeText(oldIntroducerCustomerId);
String normalizedNewIntroducerCustomerId = normalizeText(newIntroducerCustomerId);
if (Objects.equals(normalizedOldIntroducerCustomerId, normalizedNewIntroducerCustomerId)) {
return;
}
changeInvitedCount(normalizedOldIntroducerCustomerId, -1L);
changeInvitedCount(normalizedNewIntroducerCustomerId, 1L);
}
private void changeInvitedCount(String customerId, long delta) {
String normalizedCustomerId = normalizeText(customerId);
if (normalizedCustomerId == null || delta == 0) {
return;
}
mapper.updateInvitedCountDelta(normalizedCustomerId, delta);
}
private String normalizeText(String value) {
if (value == null) {
return null;
}
String trimmedValue = value.trim();
return trimmedValue.isEmpty() ? null : trimmedValue;
} }
private void saveAttachment(String customerId, List<Attachment> attachmentList) { private void saveAttachment(String customerId, List<Attachment> attachmentList) {
@@ -8,6 +8,8 @@ import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq;
import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.entity.GaoCustomer;
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent;
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord;
import com.imyeyu.api.modules.gao.mapper.GaoCustomerMapper;
import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventMapper;
import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventRecordMapper; import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventRecordMapper;
import com.imyeyu.api.modules.gao.service.GaoCustomerService; import com.imyeyu.api.modules.gao.service.GaoCustomerService;
import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService; import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService;
@@ -15,8 +17,10 @@ import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq; import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq;
@@ -24,6 +28,7 @@ import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordTrendStatReq;
import com.imyeyu.api.modules.user.service.RoleChecker; import com.imyeyu.api.modules.user.service.RoleChecker;
import com.imyeyu.api.modules.user.service.UserLoginService; import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.api.modules.user.service.UserService; import com.imyeyu.api.modules.user.service.UserService;
import com.imyeyu.api.modules.user.mapper.UserMapper;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.Page;
@@ -55,6 +60,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.function.Function;
import java.util.stream.Collectors;
/// ///
/// 登记事件记录服务实现 /// 登记事件记录服务实现
@@ -69,6 +76,9 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
private final GaoRegistrationEventRecordMapper mapper; private final GaoRegistrationEventRecordMapper mapper;
private final GaoCustomerMapper customerMapper;
private final GaoRegistrationEventMapper registrationEventMapper;
private final UserMapper userMapper;
private final AttachmentService attachmentService; private final AttachmentService attachmentService;
private final RoleChecker roleChecker; private final RoleChecker roleChecker;
private final UserLoginService userLoginService; private final UserLoginService userLoginService;
@@ -89,6 +99,8 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
String customerId = resolveCustomerId(req); String customerId = resolveCustomerId(req);
GaoRegistrationEvent registrationEvent = registrationEventService.get(req.getRegistrationEventId()); GaoRegistrationEvent registrationEvent = registrationEventService.get(req.getRegistrationEventId());
checkRegistrationEventPermission(registrationEvent); checkRegistrationEventPermission(registrationEvent);
long registeredAt = TimiJava.defaultIfNull(req.getRegisteredAt(), Time.now());
checkRegistrationEventCreatable(registrationEvent, customerId, registeredAt);
if (Boolean.TRUE.equals(registrationEvent.getRequirePhoto())) { if (Boolean.TRUE.equals(registrationEvent.getRequirePhoto())) {
TimiException.requiredTrue(req.getAttachmentIdList() != null && !req.getAttachmentIdList().isEmpty(), "required record photo"); TimiException.requiredTrue(req.getAttachmentIdList() != null && !req.getAttachmentIdList().isEmpty(), "required record photo");
} }
@@ -98,7 +110,7 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
record.setRegistrationEventId(req.getRegistrationEventId()); record.setRegistrationEventId(req.getRegistrationEventId());
record.setOperatorUserId(userLoginService.getRequireLoginUserId()); record.setOperatorUserId(userLoginService.getRequireLoginUserId());
record.setRemark(req.getRemark()); record.setRemark(req.getRemark());
record.setRegisteredAt(TimiJava.defaultIfNull(req.getRegisteredAt(), Time.now())); record.setRegisteredAt(registeredAt);
super.create(record); super.create(record);
saveAttachment(record.getId(), buildPhotoAttachmentList(req.getAttachmentIdList())); saveAttachment(record.getId(), buildPhotoAttachmentList(req.getAttachmentIdList()));
return get(record.getId()); return get(record.getId());
@@ -137,7 +149,7 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
@Override @Override
public GaoRegistrationEventRecord get(String id) { public GaoRegistrationEventRecord get(String id) {
GaoRegistrationEventRecord record = super.get(id); GaoRegistrationEventRecord record = super.get(id);
loadTransient(record); loadTransient(List.of(record));
return record; return record;
} }
@@ -148,7 +160,7 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
TimiException.required(req.getPeriodType(), "not found req.periodType"); TimiException.required(req.getPeriodType(), "not found req.periodType");
TimiException.required(req.getPeriodValue(), "not found req.periodValue"); TimiException.required(req.getPeriodValue(), "not found req.periodValue");
List<GaoRegistrationEventRecord> list = mapper.selectByRegistrationEventAndPeriod(req.getRegistrationEventId(), req.getPeriodType(), req.getPeriodValue()); List<GaoRegistrationEventRecord> list = mapper.selectByRegistrationEventAndPeriod(req.getRegistrationEventId(), req.getPeriodType(), req.getPeriodValue());
list.forEach(this::loadTransient); loadTransient(list);
return list; return list;
} }
@@ -157,12 +169,19 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
TimiException.required(req, "not found req"); TimiException.required(req, "not found req");
checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt());
List<GaoRegistrationEventRecord> list = mapper.selectByRange(req); List<GaoRegistrationEventRecord> list = mapper.selectByRange(req);
list.forEach(this::loadTransient); loadTransient(list);
return list; return list;
} }
@Override @Override
public PageResult<GaoRegistrationEventRecord> pageByRange(Page<GaoRegistrationEventRecordRangeReq> page) { public List<GaoRegistrationEventRecordCalendarView> listCalendarByRange(GaoRegistrationEventRecordRangeReq req) {
TimiException.required(req, "not found req");
checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt());
return mapper.selectCalendarByRange(req);
}
@Override
public PageResult<GaoRegistrationEventRecordListItemView> pageByRange(Page<GaoRegistrationEventRecordRangeReq> page) {
TimiException.required(page, "not found page"); TimiException.required(page, "not found page");
TimiException.required(page.getIndex(), "not found page.index"); TimiException.required(page.getIndex(), "not found page.index");
TimiException.required(page.getSize(), "not found page.size"); TimiException.required(page.getSize(), "not found page.size");
@@ -171,9 +190,8 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
GaoRegistrationEventRecordRangeReq req = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoRegistrationEventRecordRangeReq()); GaoRegistrationEventRecordRangeReq req = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoRegistrationEventRecordRangeReq());
checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt());
Long total = TimiJava.defaultIfNull(mapper.countByRange(req), 0L); Long total = TimiJava.defaultIfNull(mapper.countByRange(req), 0L);
List<GaoRegistrationEventRecord> list = mapper.selectPageByRange(req, page.getIndex() * page.getSize(), page.getSize()); List<GaoRegistrationEventRecordListItemView> list = mapper.selectPageByRange(req, page.getIndex() * page.getSize(), page.getSize());
list.forEach(this::loadTransient); PageResult<GaoRegistrationEventRecordListItemView> result = new PageResult<>();
PageResult<GaoRegistrationEventRecord> result = new PageResult<>();
result.setTotal(total); result.setTotal(total);
result.setList(list); result.setList(list);
return result; return result;
@@ -236,7 +254,7 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
return Collections.emptyList(); return Collections.emptyList();
} }
List<GaoRegistrationEventRecord> list = mapper.selectLatestByCustomerIdListAndRegistrationEventId(customerIdList, registrationEventId); List<GaoRegistrationEventRecord> list = mapper.selectLatestByCustomerIdListAndRegistrationEventId(customerIdList, registrationEventId);
list.forEach(this::loadTransient); loadTransient(list);
return list; return list;
} }
@@ -279,7 +297,7 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
public List<GaoRegistrationEventRecord> listLatestByCustomerId(String customerId) { public List<GaoRegistrationEventRecord> listLatestByCustomerId(String customerId) {
TimiException.required(customerId, "not found customerId"); TimiException.required(customerId, "not found customerId");
List<GaoRegistrationEventRecord> list = mapper.selectLatestByCustomerId(customerId); List<GaoRegistrationEventRecord> list = mapper.selectLatestByCustomerId(customerId);
list.forEach(this::loadTransient); loadTransient(list);
return list; return list;
} }
@@ -300,6 +318,76 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
} }
} }
private void checkRegistrationEventCreatable(GaoRegistrationEvent registrationEvent, String customerId, long registeredAt) {
TimiException.requiredTrue(Boolean.TRUE.equals(registrationEvent.getEnabled()), "登记事件未启用");
TimiException.requiredTrue(registrationEvent.getStatus() == null || registrationEvent.getStatus() == GaoRegistrationEvent.Status.ACTIVE, "登记事件不可登记");
GaoRegistrationEvent.RegistrationLimitType limitType = TimiJava.defaultIfNull(registrationEvent.getLimitType(), GaoRegistrationEvent.RegistrationLimitType.NONE);
switch (limitType) {
case DAILY_HALF_DAY -> checkRegistrationLimitRange(customerId, registrationEvent.getId(), getHalfDayBegin(registeredAt), getHalfDayEnd(registeredAt), "该客户当前半日已登记该事件");
case NATURAL_DAY -> checkRegistrationLimitRange(customerId, registrationEvent.getId(), getDayBegin(registeredAt), getDayEnd(registeredAt), "该客户当日已登记该事件");
case INTERVAL_HOUR -> checkIntervalHourRegistrationLimit(registrationEvent, customerId, registeredAt);
case NATURAL_MONTH -> checkRegistrationLimitRange(customerId, registrationEvent.getId(), getMonthBegin(registeredAt), getMonthEnd(registeredAt), "该客户当月已登记该事件");
case TOTAL_LIMIT -> checkTotalRegistrationLimit(registrationEvent, customerId);
default -> {
}
}
}
private void checkRegistrationLimitRange(String customerId, String registrationEventId, long beginRegisteredAt, long endRegisteredAt, String message) {
Long count = mapper.countByCustomerIdAndRegistrationEventIdRange(customerId, registrationEventId, beginRegisteredAt, endRegisteredAt);
TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < 1, message);
}
private void checkIntervalHourRegistrationLimit(GaoRegistrationEvent registrationEvent, String customerId, long registeredAt) {
Integer limitValue = registrationEvent.getLimitValue();
TimiException.requiredTrue(limitValue != null && 0 < limitValue, "登记事件间隔小时配置错误");
long beginRegisteredAt = registeredAt - limitValue * 60L * 60L * 1000L + 1;
checkRegistrationLimitRange(customerId, registrationEvent.getId(), beginRegisteredAt, registeredAt, "该客户登记该事件间隔不足");
}
private void checkTotalRegistrationLimit(GaoRegistrationEvent registrationEvent, String customerId) {
Integer limitValue = registrationEvent.getLimitValue();
TimiException.requiredTrue(limitValue != null && 0 < limitValue, "登记事件累计次数配置错误");
Long count = mapper.countByCustomerIdAndRegistrationEventId(customerId, registrationEvent.getId());
TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < limitValue, "该客户登记该事件次数已达上限");
}
private long getDayBegin(long registeredAt) {
ZoneId zoneId = ZoneId.systemDefault();
return Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli();
}
private long getDayEnd(long registeredAt) {
ZoneId zoneId = ZoneId.systemDefault();
return Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate().plusDays(1).atStartOfDay(zoneId).toInstant().toEpochMilli() - 1;
}
private long getHalfDayBegin(long registeredAt) {
ZoneId zoneId = ZoneId.systemDefault();
int hour = Instant.ofEpochMilli(registeredAt).atZone(zoneId).getHour();
long dayBegin = getDayBegin(registeredAt);
return hour < 12 ? dayBegin : dayBegin + 12L * 60L * 60L * 1000L;
}
private long getHalfDayEnd(long registeredAt) {
ZoneId zoneId = ZoneId.systemDefault();
int hour = Instant.ofEpochMilli(registeredAt).atZone(zoneId).getHour();
long dayBegin = getDayBegin(registeredAt);
return hour < 12 ? dayBegin + 12L * 60L * 60L * 1000L - 1 : getDayEnd(registeredAt);
}
private long getMonthBegin(long registeredAt) {
ZoneId zoneId = ZoneId.systemDefault();
LocalDate date = Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate();
return date.withDayOfMonth(1).atStartOfDay(zoneId).toInstant().toEpochMilli();
}
private long getMonthEnd(long registeredAt) {
ZoneId zoneId = ZoneId.systemDefault();
LocalDate date = Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate();
return date.withDayOfMonth(1).plusMonths(1).atStartOfDay(zoneId).toInstant().toEpochMilli() - 1;
}
private void checkStatRange(Long beginRegisteredAt, Long endRegisteredAt) { private void checkStatRange(Long beginRegisteredAt, Long endRegisteredAt) {
TimiException.required(beginRegisteredAt, "not found beginRegisteredAt"); TimiException.required(beginRegisteredAt, "not found beginRegisteredAt");
TimiException.required(endRegisteredAt, "not found endRegisteredAt"); TimiException.required(endRegisteredAt, "not found endRegisteredAt");
@@ -340,16 +428,51 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
return list; return list;
} }
private void loadTransient(GaoRegistrationEventRecord record) { private void loadTransient(List<GaoRegistrationEventRecord> recordList) {
if (record == null) { if (recordList == null || recordList.isEmpty()) {
return; return;
} }
record.setCustomer(customerService.get(record.getCustomerId())); List<String> customerIdList = recordList.stream()
record.setRegistrationEvent(registrationEventService.get(record.getRegistrationEventId())); .map(GaoRegistrationEventRecord::getCustomerId)
if (TimiJava.isNotEmpty(record.getOperatorUserId())) { .filter(TimiJava::isNotEmpty)
record.setOperator(userService.get(record.getOperatorUserId())); .distinct()
.toList();
Map<String, GaoCustomer> customerMap = customerMapper.selectByIdList(new ArrayList<>(customerIdList)).stream()
.collect(Collectors.toMap(GaoCustomer::getId, Function.identity(), (left, right) -> left));
List<String> registrationEventIdList = recordList.stream()
.map(GaoRegistrationEventRecord::getRegistrationEventId)
.filter(TimiJava::isNotEmpty)
.distinct()
.toList();
Map<String, GaoRegistrationEvent> registrationEventMap = registrationEventMapper.selectByIdList(new ArrayList<>(registrationEventIdList)).stream()
.collect(Collectors.toMap(GaoRegistrationEvent::getId, Function.identity(), (left, right) -> left));
List<String> operatorUserIdList = recordList.stream()
.map(GaoRegistrationEventRecord::getOperatorUserId)
.filter(TimiJava::isNotEmpty)
.distinct()
.toList();
Map<String, com.imyeyu.api.modules.user.entity.User> userMap = userMapper.listByIdList(new ArrayList<>(operatorUserIdList)).stream()
.collect(Collectors.toMap(com.imyeyu.api.modules.user.entity.User::getId, Function.identity(), (left, right) -> left));
List<String> recordIdList = recordList.stream()
.map(GaoRegistrationEventRecord::getId)
.filter(TimiJava::isNotEmpty)
.distinct()
.toList();
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
Attachment.BizType.GAO_REGISTRATION_EVENT_RECORD,
recordIdList,
GaoRegistrationEventRecord.AttachType.PHOTO.name()
);
for (GaoRegistrationEventRecord record : recordList) {
record.setCustomer(customerMap.get(record.getCustomerId()));
record.setRegistrationEvent(registrationEventMap.get(record.getRegistrationEventId()));
record.setOperator(userMap.get(record.getOperatorUserId()));
record.setAttachmentList(attachmentMap.getOrDefault(record.getId(), List.of()));
} }
record.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_REGISTRATION_EVENT_RECORD, record.getId(), GaoRegistrationEventRecord.AttachType.PHOTO.name()));
} }
private void checkDailyStatRange(LocalDate beginDate, LocalDate endDate) { private void checkDailyStatRange(LocalDate beginDate, LocalDate endDate) {
@@ -15,9 +15,12 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
/// ///
/// 登记事件服务实现 /// 登记事件服务实现
@@ -39,9 +42,10 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
@Override @Override
public PageResult<GaoRegistrationEvent> pageWithRegistrationEventLimit(Page<GaoRegistrationEvent> page) { public PageResult<GaoRegistrationEvent> pageWithRegistrationEventLimit(Page<GaoRegistrationEvent> page) {
PageResult<GaoRegistrationEvent> result = page(page); PageResult<GaoRegistrationEvent> result = page(page);
if (result.getList() != null) { result.setList(result.getList().stream()
result.getList().forEach(this::loadRegistrationEventLimit); .filter(item -> item.getStatus() != GaoRegistrationEvent.Status.DELETED)
} .toList());
loadRegistrationEventLimit(result.getList());
return result; return result;
} }
@@ -64,8 +68,10 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
@Transactional(TimiServerDBConfig.ROLLBACKER) @Transactional(TimiServerDBConfig.ROLLBACKER)
@Override @Override
public void delete(String id) { public void delete(String id) {
roleRelationMapper.deleteByRegistrationEventId(id); GaoRegistrationEvent entity = get(id);
super.delete(id); entity.setStatus(GaoRegistrationEvent.Status.DELETED);
entity.setEnabled(false);
super.update(entity);
} }
@Override @Override
@@ -78,7 +84,7 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
@Override @Override
public List<GaoRegistrationEvent> listEnabled() { public List<GaoRegistrationEvent> listEnabled() {
List<GaoRegistrationEvent> list = mapper.selectEnabledList(); List<GaoRegistrationEvent> list = mapper.selectEnabledList();
list.forEach(this::loadRegistrationEventLimit); loadRegistrationEventLimit(list);
return list; return list;
} }
@@ -93,17 +99,37 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
TimiException.required(entity.getId(), "not found registrationEvent.id"); TimiException.required(entity.getId(), "not found registrationEvent.id");
} }
TimiException.required(entity.getName(), "not found registrationEvent.name"); TimiException.required(entity.getName(), "not found registrationEvent.name");
if (entity.getStatus() == null) {
entity.setStatus(GaoRegistrationEvent.Status.ACTIVE);
}
if (entity.getEnabled() == null) { if (entity.getEnabled() == null) {
entity.setEnabled(true); entity.setEnabled(true);
} }
if (entity.getRequirePhoto() == null) { if (entity.getRequirePhoto() == null) {
entity.setRequirePhoto(false); entity.setRequirePhoto(false);
} }
if (entity.getLimitType() == null) {
entity.setLimitType(GaoRegistrationEvent.RegistrationLimitType.NONE);
}
checkRegistrationLimit(entity);
if (entity.getColor() != null) {
entity.setColor(entity.getColor().trim());
}
if (entity.getSort() == null) { if (entity.getSort() == null) {
entity.setSort(0); entity.setSort(0);
} }
} }
private void checkRegistrationLimit(GaoRegistrationEvent entity) {
if (entity.getLimitType() == GaoRegistrationEvent.RegistrationLimitType.INTERVAL_HOUR
|| entity.getLimitType() == GaoRegistrationEvent.RegistrationLimitType.TOTAL_LIMIT) {
TimiException.required(entity.getLimitValue(), "not found registrationEvent.limitValue");
TimiException.requiredTrue(0 < entity.getLimitValue(), "registrationEvent.limitValue lte 0");
return;
}
entity.setLimitValue(null);
}
private void saveRoleRelation(String registrationEventId, List<String> roleCodeList) { private void saveRoleRelation(String registrationEventId, List<String> roleCodeList) {
roleRelationMapper.deleteByRegistrationEventId(registrationEventId); roleRelationMapper.deleteByRegistrationEventId(registrationEventId);
if (roleCodeList == null || roleCodeList.isEmpty()) { if (roleCodeList == null || roleCodeList.isEmpty()) {
@@ -124,4 +150,22 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
private void loadRegistrationEventLimit(GaoRegistrationEvent registrationEvent) { private void loadRegistrationEventLimit(GaoRegistrationEvent registrationEvent) {
registrationEvent.setRoleCodeList(roleRelationMapper.selectRoleCodeListByRegistrationEventId(registrationEvent.getId())); registrationEvent.setRoleCodeList(roleRelationMapper.selectRoleCodeListByRegistrationEventId(registrationEvent.getId()));
} }
private void loadRegistrationEventLimit(List<GaoRegistrationEvent> registrationEventList) {
if (registrationEventList == null || registrationEventList.isEmpty()) {
return;
}
List<String> registrationEventIdList = registrationEventList.stream()
.map(GaoRegistrationEvent::getId)
.distinct()
.toList();
Map<String, List<String>> roleCodeMap = roleRelationMapper.selectByRegistrationEventIdList(new ArrayList<>(registrationEventIdList)).stream()
.collect(Collectors.groupingBy(
GaoRegistrationEventRoleRelation::getRegistrationEventId,
Collectors.mapping(GaoRegistrationEventRoleRelation::getRoleCode, Collectors.toList())
));
for (GaoRegistrationEvent registrationEvent : registrationEventList) {
registrationEvent.setRoleCodeList(roleCodeMap.getOrDefault(registrationEvent.getId(), List.of()));
}
}
} }
@@ -0,0 +1,18 @@
package com.imyeyu.api.modules.gao.vo;
import lombok.Data;
///
/// 客户性别统计视图
///
/// @author Codex
/// @since 2026-08-01
@Data
public class GaoCustomerGenderStatView {
/// 性别编码,未知时为 UNKNOWN
private String genderCode;
/// 客户数量
private Long count;
}
@@ -0,0 +1,30 @@
package com.imyeyu.api.modules.gao.vo;
import lombok.Data;
///
/// 登记事件记录日历轻量视图
///
/// @author Codex
/// @since 2026-08-01
@Data
public class GaoRegistrationEventRecordCalendarView {
/// 记录 ID
private String id;
/// 客户 ID
private String customerId;
/// 客户编码
private String customerCode;
/// 客户姓名
private String customerName;
/// 登记事件 ID
private String registrationEventId;
/// 登记时间
private Long registeredAt;
}
@@ -0,0 +1,33 @@
package com.imyeyu.api.modules.gao.vo;
import lombok.Data;
///
/// 登记事件记录列表项视图
///
/// @author Codex
/// @since 2026-08-01
@Data
public class GaoRegistrationEventRecordListItemView {
/// 记录 ID
private String id;
/// 客户 ID
private String customerId;
/// 客户姓名
private String customerName;
/// 客户头像附件 ID
private String customerPhotoAttachmentId;
/// 登记事件 ID
private String registrationEventId;
/// 登记事件名称
private String registrationEventName;
/// 登记时间
private Long registeredAt;
}
@@ -1,22 +0,0 @@
package com.imyeyu.api.modules.gao.vo;
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
///
/// 登记事件保存请求
///
/// @author Codex
/// @since 2026-07-27
@Data
public class GaoRegistrationEventSaveReq {
/// 登记事件
private GaoRegistrationEvent registrationEvent;
/// 可登记角色代码列表
private List<String> roleCodeList = new ArrayList<>();
}
@@ -0,0 +1,3 @@
ALTER TABLE `gao_customer`
ADD COLUMN `creator_user_id` VARCHAR(36) NULL COMMENT '录入用户 ID' AFTER `introducer_customer_id`,
ADD KEY `idx_gao_customer_creator_user_id` (`creator_user_id`);
@@ -0,0 +1,12 @@
ALTER TABLE `gao_customer`
ADD COLUMN `invited_count` BIGINT NOT NULL DEFAULT 0 COMMENT '邀请客户数' AFTER `introducer_customer_id`;
UPDATE `gao_customer` AS `customer`
SET `invited_count` = (
SELECT COUNT(1)
FROM `gao_customer` AS `invitee`
WHERE
`invitee`.`introducer_customer_id` = `customer`.`id`
AND `invitee`.`deleted_at` IS NULL
)
WHERE `customer`.`deleted_at` IS NULL;
@@ -0,0 +1,6 @@
ALTER TABLE `gao_registration_event`
ADD COLUMN `color` VARCHAR(32) NULL COMMENT '统计图表颜色' AFTER `id`,
ADD COLUMN `status` VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' COMMENT '状态,ACTIVE 可登记,INACTIVE 无效,DELETED 已删除' AFTER `sort`,
ADD COLUMN `limit_type` VARCHAR(32) NOT NULL DEFAULT 'NONE' COMMENT '登记限制类型' AFTER `require_photo`,
ADD COLUMN `limit_value` INT NULL COMMENT '登记限制数值,按限制类型表示小时数或次数' AFTER `limit_type`,
ADD KEY `idx_gao_registration_event_status` (`status`, `enabled`, `deleted_at`, `sort`);
@@ -0,0 +1,29 @@
<?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.common.mapper.AttachmentMapper">
<select id="selectByBizIdList" resultType="com.imyeyu.api.modules.common.entity.Attachment">
SELECT
*
FROM `attachment`
WHERE
`biz_type` = #{bizType}
<if test="bizIdList != null and !bizIdList.isEmpty()">
AND `biz_id` IN
<foreach collection="bizIdList" item="bizId" separator="," open="(" close=")">
#{bizId}
</foreach>
</if>
<if test="bizIdList == null or bizIdList.isEmpty()">
AND 1 = 0
</if>
AND `deleted_at` IS NULL
AND (`destroy_at` IS NULL OR UNIX_TIMESTAMP() &lt; `destroy_at`)
<if test="attachTypes != null and !attachTypes.isEmpty()">
AND `attach_type` IN
<foreach collection="attachTypes" item="attachType" separator="," open="(" close=")">
#{attachType}
</foreach>
</if>
ORDER BY `created_at` ASC
</select>
</mapper>
@@ -28,6 +28,18 @@
ORDER BY `created_at` DESC ORDER BY `created_at` DESC
</select> </select>
<select id="selectByIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoCustomer">
SELECT
*
FROM `gao_customer`
WHERE
`id` IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND `deleted_at` IS NULL
</select>
<select id="countSearch" resultType="java.lang.Long"> <select id="countSearch" resultType="java.lang.Long">
SELECT SELECT
COUNT(1) COUNT(1)
@@ -65,6 +77,16 @@
AND `deleted_at` IS NULL AND `deleted_at` IS NULL
</select> </select>
<update id="updateInvitedCountDelta">
UPDATE `gao_customer`
SET
`invited_count` = GREATEST(IFNULL(`invited_count`, 0) + #{delta}, 0),
`updated_at` = UNIX_TIMESTAMP() * 1000
WHERE
`id` = #{customerId}
AND `deleted_at` IS NULL
</update>
<select id="statDailyNewCustomer" resultType="com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView"> <select id="statDailyNewCustomer" resultType="com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView">
SELECT SELECT
CAST(DATE_FORMAT(FROM_UNIXTIME(`created_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`, CAST(DATE_FORMAT(FROM_UNIXTIME(`created_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`,
@@ -86,4 +108,14 @@
`created_at` &lt; #{beginCreatedAt} `created_at` &lt; #{beginCreatedAt}
AND `deleted_at` IS NULL AND `deleted_at` IS NULL
</select> </select>
<select id="statGender" resultType="com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView">
SELECT
IFNULL(CAST(`gender` AS CHAR), 'UNKNOWN') AS `genderCode`,
COUNT(1) AS `count`
FROM `gao_customer`
WHERE
`deleted_at` IS NULL
GROUP BY IFNULL(CAST(`gender` AS CHAR), 'UNKNOWN')
</select>
</mapper> </mapper>
@@ -0,0 +1,21 @@
<?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.GaoRegistrationEventMapper">
<select id="selectByIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent">
SELECT
*
FROM `gao_registration_event`
WHERE
1 = 1
<if test="idList != null and !idList.isEmpty()">
AND `id` IN
<foreach collection="idList" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="idList == null or idList.isEmpty()">
AND 1 = 0
</if>
AND `deleted_at` IS NULL
</select>
</mapper>
@@ -20,6 +20,28 @@
ORDER BY `registered_at` DESC ORDER BY `registered_at` DESC
</select> </select>
<select id="countByCustomerIdAndRegistrationEventIdRange" resultType="java.lang.Long">
SELECT
COUNT(1)
FROM `gao_registration_event_record`
WHERE
`customer_id` = #{customerId}
AND `registration_event_id` = #{registrationEventId}
AND `registered_at` &gt;= #{beginRegisteredAt}
AND `registered_at` &lt;= #{endRegisteredAt}
AND `deleted_at` IS NULL
</select>
<select id="countByCustomerIdAndRegistrationEventId" resultType="java.lang.Long">
SELECT
COUNT(1)
FROM `gao_registration_event_record`
WHERE
`customer_id` = #{customerId}
AND `registration_event_id` = #{registrationEventId}
AND `deleted_at` IS NULL
</select>
<select id="statByRegistrationEventAndPeriodType" resultType="com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView"> <select id="statByRegistrationEventAndPeriodType" resultType="com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView">
SELECT SELECT
`registration_event_id` AS `registrationEventId`, `registration_event_id` AS `registrationEventId`,
@@ -259,6 +281,20 @@
ORDER BY r.`registered_at` DESC, r.`created_at` DESC ORDER BY r.`registered_at` DESC, r.`created_at` DESC
</select> </select>
<select id="selectCalendarByRange" resultType="com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView">
SELECT
r.`id` AS `id`,
r.`customer_id` AS `customerId`,
c.`code` AS `customerCode`,
c.`name` AS `customerName`,
r.`registration_event_id` AS `registrationEventId`,
r.`registered_at` AS `registeredAt`
<include refid="rangeFrom"/>
WHERE
<include refid="rangeWhere"/>
ORDER BY r.`registered_at` DESC, r.`created_at` DESC
</select>
<select id="countByRange" resultType="java.lang.Long"> <select id="countByRange" resultType="java.lang.Long">
SELECT SELECT
COUNT(1) COUNT(1)
@@ -267,9 +303,26 @@
<include refid="rangeWhere"/> <include refid="rangeWhere"/>
</select> </select>
<select id="selectPageByRange" resultType="com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord"> <select id="selectPageByRange" resultType="com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView">
SELECT SELECT
r.* r.`id` AS `id`,
r.`customer_id` AS `customerId`,
c.`name` AS `customerName`,
(
SELECT a.`id`
FROM `attachment` a
WHERE
a.`biz_type` = 'GAO_CUSTOMER'
AND a.`biz_id` = c.`id`
AND a.`attach_type` = 'PHOTO'
AND a.`deleted_at` IS NULL
AND (a.`destroy_at` IS NULL OR UNIX_TIMESTAMP() &lt; a.`destroy_at`)
ORDER BY a.`created_at` ASC
LIMIT 1
) AS `customerPhotoAttachmentId`,
r.`registration_event_id` AS `registrationEventId`,
e.`name` AS `registrationEventName`,
r.`registered_at` AS `registeredAt`
<include refid="rangeFrom"/> <include refid="rangeFrom"/>
WHERE WHERE
<include refid="rangeWhere"/> <include refid="rangeWhere"/>