Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
944de228f7 | ||
|
|
a81c8476e7 | ||
|
|
25c9cd4dfc | ||
|
|
e7cf1a72e3 | ||
|
|
882cd226ec | ||
|
|
eec665109e | ||
|
|
f6a2f5a6f5 | ||
|
|
9ba610de86 | ||
|
|
e59dc363f4 | ||
|
|
eeb7b5b32e | ||
|
|
b4b6a13f8d | ||
|
|
4a7de1909c | ||
|
|
b7fcecfa7b | ||
|
|
3b68e6dd85 | ||
|
|
feb4d7af66 | ||
|
|
5cfcd24657 | ||
|
|
712118556b | ||
|
|
d75ab1ed5f | ||
|
|
4496619eeb | ||
|
|
c85b400a94 | ||
|
|
94c99e3444 | ||
|
|
44c2095d77 | ||
|
|
a501eb7443 | ||
|
|
35ec38ed9c | ||
|
|
2b5be16ad7 | ||
|
|
d747a0fdae | ||
|
|
9f884e2b74 | ||
|
|
0239697a2b | ||
|
|
2b11bdf65e | ||
|
|
19941d9480 | ||
|
|
81933d4e61 | ||
|
|
43d76040d8 |
@@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>com.imyeyu.timiserverapi</groupId>
|
||||
<artifactId>TimiServerAPI</artifactId>
|
||||
<version>1.0.24</version>
|
||||
<version>1.0.30</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>TimiServerAPI</name>
|
||||
<description>imyeyu.com API</description>
|
||||
@@ -198,7 +198,7 @@
|
||||
<dependency>
|
||||
<groupId>com.imyeyu.java</groupId>
|
||||
<artifactId>timi-java</artifactId>
|
||||
<version>0.0.7</version>
|
||||
<version>0.0.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.imyeyu.utils</groupId>
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
||||
import com.imyeyu.api.modules.common.service.NotifyDetailService;
|
||||
import com.imyeyu.api.modules.common.service.NotifyService;
|
||||
import com.imyeyu.api.modules.common.service.SmsService;
|
||||
import com.imyeyu.api.modules.common.vo.NotifyManagementItem;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.api.modules.user.service.UserService;
|
||||
@@ -70,25 +71,31 @@ public class NotifyController {
|
||||
@RequiredToken
|
||||
@RequestMapping("/detail")
|
||||
public Notify detail(@RequestParam String id) {
|
||||
Notify notify = service.get(id);
|
||||
{
|
||||
List<NotifyDetail> detailList = detailService.listByNotifyId(id);
|
||||
Set<String> userIdSet = detailList.stream()
|
||||
.filter(item -> item.getMsgType() == NotifyDetail.MsgType.INTERNAL)
|
||||
.flatMap(item -> Stream.of(item.getSendFrom(), item.getSendTo()))
|
||||
.collect(Collectors.toSet());
|
||||
Map<String, User> userMap = userService.mapByIdList(userIdSet);
|
||||
for (NotifyDetail detail : detailList) {
|
||||
if (TimiJava.isNotEmpty(detail.getSendFrom())) {
|
||||
detail.setFromUser(userMap.get(detail.getSendFrom()));
|
||||
}
|
||||
if (TimiJava.isNotEmpty(detail.getSendTo())) {
|
||||
detail.setToUser(userMap.get(detail.getSendTo()));
|
||||
}
|
||||
}
|
||||
notify.setDetailList(detailList);
|
||||
}
|
||||
return notify;
|
||||
return fillDetailList(service.get(id));
|
||||
}
|
||||
|
||||
/// 查询管理端通知列表
|
||||
///
|
||||
/// @param page 分页参数
|
||||
/// @return 管理端通知列表
|
||||
@JsonView(ResponseView.Admin.class)
|
||||
@RequireCorePermission(CorePermissionCode.NOTIFY_READ)
|
||||
@RequiredToken
|
||||
@PostMapping("/management/list")
|
||||
public PageResult<NotifyManagementItem> managementList(@RequestBody(required = false) Page<Notify> page) {
|
||||
return service.pageManagement(page, userLoginService.getRequireLoginUserId());
|
||||
}
|
||||
|
||||
/// 查询管理端通知详情及收件人
|
||||
///
|
||||
/// @param id 通知 ID
|
||||
/// @return 通知详情及收件人
|
||||
@JsonView(ResponseView.Admin.class)
|
||||
@RequireCorePermission(CorePermissionCode.NOTIFY_READ)
|
||||
@RequiredToken
|
||||
@PostMapping("/management/detail")
|
||||
public Notify managementDetail(@RequestParam String id) {
|
||||
return fillDetailList(service.getManagement(id, userLoginService.getRequireLoginUserId()));
|
||||
}
|
||||
|
||||
/// 删除通知及其通知详情
|
||||
@@ -111,6 +118,26 @@ public class NotifyController {
|
||||
service.cancel(id);
|
||||
}
|
||||
|
||||
/// 删除管理端通知及其通知详情
|
||||
///
|
||||
/// @param id 通知 ID
|
||||
@RequireCorePermission(CorePermissionCode.NOTIFY_DELETE)
|
||||
@RequiredToken
|
||||
@PostMapping("/management/delete")
|
||||
public void managementDelete(@RequestParam String id) {
|
||||
service.deleteManagement(id, userLoginService.getRequireLoginUserId());
|
||||
}
|
||||
|
||||
/// 取消管理端尚未成功发送的通知
|
||||
///
|
||||
/// @param id 通知 ID
|
||||
@RequireCorePermission(CorePermissionCode.NOTIFY_CANCEL)
|
||||
@RequiredToken
|
||||
@PostMapping("/management/cancel")
|
||||
public void managementCancel(@RequestParam String id) {
|
||||
service.cancelManagement(id, userLoginService.getRequireLoginUserId());
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@CaptchaValid
|
||||
@RequestRateLimit(value = 1, inSeconds = 59)
|
||||
@@ -149,14 +176,14 @@ public class NotifyController {
|
||||
return detail;
|
||||
}
|
||||
|
||||
/// 删除当前用户的一条站内通知
|
||||
/// 隐藏当前用户的一条站内通知
|
||||
///
|
||||
/// @param id 通知详情 ID
|
||||
@RequireCorePermission(CorePermissionCode.NOTIFY_READ)
|
||||
@RequiredToken
|
||||
@PostMapping("/internal/delete")
|
||||
public void deleteInternal(@RequestParam String id) {
|
||||
detailService.deleteInternalByUserId(id, userLoginService.getRequireLoginUserId());
|
||||
detailService.hideInternalByUserId(id, userLoginService.getRequireLoginUserId());
|
||||
}
|
||||
|
||||
/// 查询当前用户未读通知数量
|
||||
@@ -186,4 +213,30 @@ public class NotifyController {
|
||||
public void markAllSiteRead() {
|
||||
detailService.markAllSiteRead(userLoginService.getRequireLoginUserId());
|
||||
}
|
||||
|
||||
/// 回填通知详情中的用户信息
|
||||
///
|
||||
/// @param notify 通知
|
||||
/// @return 已回填用户信息的通知
|
||||
private Notify fillDetailList(Notify notify) {
|
||||
if (notify == null) {
|
||||
return null;
|
||||
}
|
||||
List<NotifyDetail> detailList = detailService.listByNotifyId(notify.getId());
|
||||
Set<String> userIdSet = detailList.stream()
|
||||
.filter(item -> item.getMsgType() == NotifyDetail.MsgType.INTERNAL)
|
||||
.flatMap(item -> Stream.of(item.getSendFrom(), item.getSendTo()))
|
||||
.collect(Collectors.toSet());
|
||||
Map<String, User> userMap = userService.mapByIdList(userIdSet);
|
||||
for (NotifyDetail detail : detailList) {
|
||||
if (TimiJava.isNotEmpty(detail.getSendFrom())) {
|
||||
detail.setFromUser(userMap.get(detail.getSendFrom()));
|
||||
}
|
||||
if (TimiJava.isNotEmpty(detail.getSendTo())) {
|
||||
detail.setToUser(userMap.get(detail.getSendTo()));
|
||||
}
|
||||
}
|
||||
notify.setDetailList(detailList);
|
||||
return notify;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.api.modules.common.bean.attachment.SettingSupport;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.spring.annotation.table.PageIgnore;
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
@@ -30,6 +31,9 @@ public class Article extends UUIDEntity implements SettingSupport {
|
||||
/** 博客 */
|
||||
BLOG,
|
||||
|
||||
/// GAO 门店日记
|
||||
GAO_STORE_DIARY,
|
||||
|
||||
/// 反馈
|
||||
FEEDBACK
|
||||
}
|
||||
@@ -93,6 +97,12 @@ public class Article extends UUIDEntity implements SettingSupport {
|
||||
/** 文章状态 */
|
||||
protected Status status;
|
||||
|
||||
/// 最后更新用户 ID
|
||||
protected String updatedBy;
|
||||
|
||||
@Transient
|
||||
protected User updater;
|
||||
|
||||
@Transient
|
||||
protected List<Setting> settingList;
|
||||
|
||||
|
||||
@@ -32,12 +32,18 @@ public class Attachment extends UUIDEntity {
|
||||
|
||||
ARTICLE,
|
||||
|
||||
NOTIFY_DETAIL,
|
||||
|
||||
USER,
|
||||
|
||||
GAO_CUSTOMER,
|
||||
|
||||
GAO_EVENT_RECORD,
|
||||
|
||||
GAO_PACKAGE_DELIVERY,
|
||||
|
||||
GAO_FUND_RECORD,
|
||||
|
||||
TEMP_FILE,
|
||||
|
||||
MIRROR
|
||||
|
||||
@@ -26,7 +26,7 @@ public class BaseQueueEntity implements IDEntity<String>, Creatable, Destroyable
|
||||
protected String notifyDetailId;
|
||||
|
||||
/** 内容 */
|
||||
protected String data;
|
||||
protected String content;
|
||||
|
||||
/** 参数 */
|
||||
protected JsonNode args;
|
||||
@@ -51,7 +51,7 @@ public class BaseQueueEntity implements IDEntity<String>, Creatable, Destroyable
|
||||
/// @param detail 通知详情
|
||||
public BaseQueueEntity(NotifyDetail detail) {
|
||||
notifyDetailId = detail.getId();
|
||||
data = detail.getData();
|
||||
content = detail.getContent();
|
||||
args = detail.getArgs();
|
||||
sendTo = detail.getSendTo();
|
||||
sendAt = detail.getSendAt();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.imyeyu.api.TimiServerAPI;
|
||||
@@ -16,6 +17,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通知详情
|
||||
*
|
||||
@@ -82,6 +85,19 @@ public class NotifyDetail extends UUIDEntity {
|
||||
FAIL
|
||||
}
|
||||
|
||||
/// 内容类型
|
||||
///
|
||||
/// @author 夜雨
|
||||
/// @since 2026-08-24
|
||||
public enum ContentType {
|
||||
|
||||
/** 纯文本 */
|
||||
TEXT,
|
||||
|
||||
/** TipTap 富文本 */
|
||||
TIP_TAP
|
||||
}
|
||||
|
||||
/** 所属通知 ID */
|
||||
@JsonView(ResponseView.Public.class)
|
||||
private String notifyId;
|
||||
@@ -100,7 +116,11 @@ public class NotifyDetail extends UUIDEntity {
|
||||
|
||||
/** 内容 */
|
||||
@JsonView(ResponseView.Public.class)
|
||||
private String data;
|
||||
private String content;
|
||||
|
||||
/// 内容类型
|
||||
@JsonView(ResponseView.Public.class)
|
||||
private ContentType contentType;
|
||||
|
||||
/** 参数 */
|
||||
@JsonView(ResponseView.Public.class)
|
||||
@@ -138,6 +158,10 @@ public class NotifyDetail extends UUIDEntity {
|
||||
@JsonView(ResponseView.Public.class)
|
||||
private Long readAt;
|
||||
|
||||
/// 收件人隐藏时间,仅站内通知生效
|
||||
@JsonIgnore
|
||||
private Long hiddenAt;
|
||||
|
||||
@Transient
|
||||
protected Notify notify;
|
||||
|
||||
@@ -146,4 +170,9 @@ public class NotifyDetail extends UUIDEntity {
|
||||
|
||||
@Transient
|
||||
protected User toUser;
|
||||
|
||||
/// 媒体附件列表
|
||||
@JsonView(ResponseView.Public.class)
|
||||
@Transient
|
||||
protected List<Attachment> attachmentList;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ public interface AttachmentMapper extends BaseMapper<Attachment, String>, RawMap
|
||||
|
||||
List<Attachment> selectAllByBizIdList(Attachment.BizType bizType, Collection<String> bizIdList, List<String> attachTypes);
|
||||
|
||||
List<Attachment> selectImageByBizIdList(Attachment.BizType bizType, Collection<String> bizIdList, String attachType);
|
||||
|
||||
long countByBizId(Attachment.BizType bizType, String bizId, List<String> attachTypes);
|
||||
|
||||
@Select("SELECT * FROM attachment WHERE `is_destroyed` = FALSE AND `destroy_at` < " + UNIX_TIME)
|
||||
|
||||
@@ -71,6 +71,14 @@ public interface AttachmentVariantMapper {
|
||||
/// @param variant 变体
|
||||
void insert(AttachmentVariant variant);
|
||||
|
||||
/// 延长缩略图变体的过期时间
|
||||
///
|
||||
/// @param id 变体 ID
|
||||
/// @param now 当前时间
|
||||
/// @param expireAt 新的过期时间
|
||||
/// @return 更新记录数
|
||||
int updateExpireAt(@Param("id") String id, @Param("now") long now, @Param("expireAt") long expireAt);
|
||||
|
||||
/// 删除变体
|
||||
///
|
||||
/// @param id 变体 ID
|
||||
|
||||
@@ -16,35 +16,37 @@ import java.util.List;
|
||||
*/
|
||||
public interface NotifyDetailMapper extends BaseMapper<NotifyDetail, String> {
|
||||
|
||||
String NOT_HIDDEN = " AND `hidden_at` IS NULL";
|
||||
|
||||
/** 查询用户的站内通知 */
|
||||
@Select("SELECT * FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} " + NOT_DELETE + " ORDER BY `created_at` DESC LIMIT #{offset}, #{limit}")
|
||||
@Select("SELECT * FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + NOT_HIDDEN + NOT_DELETE + " ORDER BY `created_at` DESC LIMIT #{offset}, #{limit}")
|
||||
List<NotifyDetail> selectSiteByUserId(@Param("userId") String userId, @Param("offset") long offset, @Param("limit") long limit);
|
||||
|
||||
/** 查询用户的未读站内通知 */
|
||||
@Select("SELECT * FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `read_at` IS NULL " + NOT_DELETE + " ORDER BY `created_at` DESC LIMIT #{offset}, #{limit}")
|
||||
@Select("SELECT * FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + " AND `read_at` IS NULL " + NOT_HIDDEN + NOT_DELETE + " ORDER BY `created_at` DESC LIMIT #{offset}, #{limit}")
|
||||
List<NotifyDetail> selectUnreadSiteByUserId(@Param("userId") String userId, @Param("offset") long offset, @Param("limit") long limit);
|
||||
|
||||
/** 统计用户的站内通知数量 */
|
||||
@Select("SELECT COUNT(*) FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} " + NOT_DELETE)
|
||||
@Select("SELECT COUNT(*) FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + NOT_HIDDEN + NOT_DELETE)
|
||||
long countSiteByUserId(@Param("userId") String userId);
|
||||
|
||||
/** 统计用户的未读站内通知数量 */
|
||||
@Select("SELECT COUNT(*) FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `read_at` IS NULL " + NOT_DELETE)
|
||||
@Select("SELECT COUNT(*) FROM `notify_detail` WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + " AND `read_at` IS NULL " + NOT_HIDDEN + NOT_DELETE)
|
||||
long countUnreadSiteByUserId(@Param("userId") String userId);
|
||||
|
||||
/** 查询用户拥有的站内通知 */
|
||||
@Select("SELECT * FROM `notify_detail` WHERE `id` = #{detailId} AND `msg_type` = 'INTERNAL' AND `send_to` = #{userId} " + NOT_DELETE + " LIMIT 1")
|
||||
@Select("SELECT * FROM `notify_detail` WHERE `id` = #{detailId} AND `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + NOT_HIDDEN + NOT_DELETE + " LIMIT 1")
|
||||
NotifyDetail selectSiteByIdAndUserId(@Param("detailId") String detailId, @Param("userId") String userId);
|
||||
|
||||
/** 删除用户的站内通知 */
|
||||
@Update("UPDATE `notify_detail` SET `deleted_at` = " + UNIX_TIME + ", `updated_at` = " + UNIX_TIME + " WHERE `id` = #{detailId} AND `msg_type` = 'INTERNAL' AND `send_to` = #{userId} " + NOT_DELETE)
|
||||
int deleteSiteByIdAndUserId(@Param("detailId") String detailId, @Param("userId") String userId);
|
||||
/** 隐藏用户的站内通知 */
|
||||
@Update("UPDATE `notify_detail` SET `hidden_at` = " + UNIX_TIME + ", `updated_at` = " + UNIX_TIME + " WHERE `id` = #{detailId} AND `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + NOT_HIDDEN + NOT_DELETE)
|
||||
int hideSiteByIdAndUserId(@Param("detailId") String detailId, @Param("userId") String userId);
|
||||
|
||||
/** 标记站内通知已读 */
|
||||
@Update("UPDATE `notify_detail` SET `read_at` = #{readAt}, `updated_at` = " + UNIX_TIME + " WHERE `id` = #{detailId} AND `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `read_at` IS NULL " + NOT_DELETE)
|
||||
@Update("UPDATE `notify_detail` SET `read_at` = #{readAt}, `updated_at` = " + UNIX_TIME + " WHERE `id` = #{detailId} AND `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + " AND `read_at` IS NULL " + NOT_HIDDEN + NOT_DELETE)
|
||||
int markSiteRead(@Param("detailId") String detailId, @Param("userId") String userId, @Param("readAt") long readAt);
|
||||
|
||||
/** 标记用户全部站内通知已读 */
|
||||
@Update("UPDATE `notify_detail` SET `read_at` = #{readAt}, `updated_at` = " + UNIX_TIME + " WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `read_at` IS NULL " + NOT_DELETE)
|
||||
@Update("UPDATE `notify_detail` SET `read_at` = #{readAt}, `updated_at` = " + UNIX_TIME + " WHERE `msg_type` = 'INTERNAL' AND `send_to` = #{userId} AND `send_at` < " + UNIX_TIME + " AND `read_at` IS NULL " + NOT_HIDDEN + NOT_DELETE)
|
||||
int markAllSiteRead(@Param("userId") String userId, @Param("readAt") long readAt);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Notify;
|
||||
import com.imyeyu.api.modules.common.vo.NotifyManagementItem;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通知 Mapper
|
||||
*
|
||||
@@ -10,4 +14,25 @@ import com.imyeyu.spring.mapper.BaseMapper;
|
||||
* @since 2026-05-13 14:46
|
||||
*/
|
||||
public interface NotifyMapper extends BaseMapper<Notify, String> {
|
||||
|
||||
/// 分页查询管理端通知
|
||||
///
|
||||
/// @param userId 管理员用户 ID
|
||||
/// @param offset 分页偏移量
|
||||
/// @param limit 分页大小
|
||||
/// @return 通知列表
|
||||
List<NotifyManagementItem> selectManagementPage(@Param("userId") String userId, @Param("offset") long offset, @Param("limit") long limit);
|
||||
|
||||
/// 统计管理端通知数量
|
||||
///
|
||||
/// @param userId 管理员用户 ID
|
||||
/// @return 通知数量
|
||||
long countManagement(@Param("userId") String userId);
|
||||
|
||||
/// 查询管理员可管理的通知
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
/// @param userId 管理员用户 ID
|
||||
/// @return 通知
|
||||
Notify selectManagement(@Param("notifyId") String notifyId, @Param("userId") String userId);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@ public interface AttachmentService extends BaseService<Attachment, String> {
|
||||
*/
|
||||
List<Attachment> listByBizId(Attachment.BizType bizType, String bizId, String... attachTypes);
|
||||
|
||||
default Map<String, Attachment> mapFirstImageByIdList(Attachment.BizType bizType, Collection<String> bizIdList) {
|
||||
return mapFirstImageByIdList(bizType, bizIdList, null);
|
||||
}
|
||||
|
||||
Map<String, Attachment> mapFirstImageByIdList(Attachment.BizType bizType, Collection<String> bizIdList, String attachType);
|
||||
|
||||
/**
|
||||
* 根据多个业务 ID 批量获取附件
|
||||
*
|
||||
|
||||
@@ -41,11 +41,11 @@ public interface NotifyDetailService extends BaseService<NotifyDetail, String> {
|
||||
/// @return 通知详情
|
||||
NotifyDetail getInternalByUserId(String detailId, String userId);
|
||||
|
||||
/// 删除当前用户拥有的站内通知详情
|
||||
/// 隐藏当前用户拥有的站内通知详情
|
||||
///
|
||||
/// @param detailId 通知详情 ID
|
||||
/// @param userId 用户 ID
|
||||
void deleteInternalByUserId(String detailId, String userId);
|
||||
void hideInternalByUserId(String detailId, String userId);
|
||||
|
||||
/// 查询用户未读站内通知数量
|
||||
///
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Notify;
|
||||
import com.imyeyu.api.modules.common.vo.NotifyManagementItem;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
/**
|
||||
@@ -11,6 +14,32 @@ import com.imyeyu.spring.service.BaseService;
|
||||
*/
|
||||
public interface NotifyService extends BaseService<Notify, String> {
|
||||
|
||||
/// 分页查询当前用户创建的通知
|
||||
///
|
||||
/// @param page 分页参数
|
||||
/// @param userId 用户 ID
|
||||
/// @return 管理端通知列表
|
||||
PageResult<NotifyManagementItem> pageManagement(Page<Notify> page, String userId);
|
||||
|
||||
/// 查询当前用户可管理的通知
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
/// @param userId 用户 ID
|
||||
/// @return 通知
|
||||
Notify getManagement(String notifyId, String userId);
|
||||
|
||||
/// 删除当前用户创建的通知
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
/// @param userId 用户 ID
|
||||
void deleteManagement(String notifyId, String userId);
|
||||
|
||||
/// 取消当前用户创建的通知
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
/// @param userId 用户 ID
|
||||
void cancelManagement(String notifyId, String userId);
|
||||
|
||||
/// 取消通知中尚未成功发送的通知详情
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
|
||||
+8
-1
@@ -34,10 +34,12 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -244,9 +246,14 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
||||
return mapper.selectAllByBizIdList(bizType, List.of(bizId), List.of(attachTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Attachment> mapFirstImageByIdList(Attachment.BizType bizType, Collection<String> bizIdList, String attachType) {
|
||||
return mapper.selectImageByBizIdList(bizType, new HashSet<>(bizIdList), attachType).stream().collect(Collectors.toMap(Attachment::getBizId, Function.identity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Attachment>> mapByBizIdList(Attachment.BizType bizType, Collection<String> bizIdList, String... attachTypes) {
|
||||
return mapper.selectAllByBizIdList(bizType, bizIdList, List.of(attachTypes)).stream().collect(Collectors.groupingBy(Attachment::getBizId));
|
||||
return mapper.selectAllByBizIdList(bizType, new HashSet<>(bizIdList), List.of(attachTypes)).stream().collect(Collectors.groupingBy(Attachment::getBizId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+42
-4
@@ -59,6 +59,7 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi
|
||||
private static final long THUMB_CACHE_TTL = Time.D * 7;
|
||||
private static final long THUMB_LOCK_TTL = Time.M * 10;
|
||||
private static final double THUMB_TARGET_SECONDS = 2;
|
||||
private static final int VARIANT_NAME_MAX_LENGTH = 255;
|
||||
private static final String THUMB_OUTPUT_FORMAT = "png";
|
||||
private static final String THUMB_ALGORITHM_VERSION = "v3";
|
||||
private static final String THUMB_LOCK_KEY_PREFIX = "ATTACHMENT:THUMB:LOCK:";
|
||||
@@ -112,7 +113,10 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi
|
||||
now
|
||||
);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
Attachment refreshed = refreshCachedThumb(cached);
|
||||
if (refreshed != null) {
|
||||
return refreshed;
|
||||
}
|
||||
}
|
||||
|
||||
String lockKey = buildThumbLockKey(source, width, height, fitMode);
|
||||
@@ -131,7 +135,10 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi
|
||||
Time.now()
|
||||
);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
Attachment refreshed = refreshCachedThumb(cached);
|
||||
if (refreshed != null) {
|
||||
return refreshed;
|
||||
}
|
||||
}
|
||||
AttachmentVariant oldVariant = variantMapper.selectByKey(
|
||||
source.getId(),
|
||||
@@ -152,7 +159,7 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi
|
||||
thumbFile = createThumbFile(source, requestWidth, requestHeight, fitMode, isImage);
|
||||
variant = new AttachmentVariant();
|
||||
variant.setId(UUID.randomUUID().toString());
|
||||
variant.setName(Network.simpleURIFileName(source.getName()) + "." + THUMB_OUTPUT_FORMAT);
|
||||
variant.setName(buildVariantName(source.getName()));
|
||||
variant.setMimeType("image/" + THUMB_OUTPUT_FORMAT);
|
||||
long expireAt = Time.now() + THUMB_CACHE_TTL;
|
||||
variant.setSourceAttachmentId(source.getId());
|
||||
@@ -197,7 +204,10 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi
|
||||
Time.now()
|
||||
);
|
||||
if (concurrent != null) {
|
||||
return concurrent;
|
||||
Attachment refreshed = refreshCachedThumb(concurrent);
|
||||
if (refreshed != null) {
|
||||
return refreshed;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@@ -223,6 +233,20 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi
|
||||
}
|
||||
}
|
||||
|
||||
/// 延长已命中缩略图缓存的有效期
|
||||
///
|
||||
/// @param cached 已命中的缩略图
|
||||
/// @return 更新成功时返回刷新后的缩略图,记录已不存在或已过期时返回 `null`
|
||||
private Attachment refreshCachedThumb(Attachment cached) {
|
||||
long now = Time.now();
|
||||
long expireAt = now + THUMB_CACHE_TTL;
|
||||
if (!(0 < variantMapper.updateExpireAt(cached.getId(), now, expireAt))) {
|
||||
return null;
|
||||
}
|
||||
cached.setDestroyAt(expireAt);
|
||||
return cached;
|
||||
}
|
||||
|
||||
private String buildThumbLockKey(Attachment source, int width, int height, ThumbnailFitMode fitMode) {
|
||||
return "%s%s:%s:%s:%s:%s".formatted(
|
||||
THUMB_LOCK_KEY_PREFIX,
|
||||
@@ -234,6 +258,20 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建不超过数据库字段长度的变体名称
|
||||
///
|
||||
/// @param sourceName 源附件名称
|
||||
/// @return 变体名称
|
||||
private String buildVariantName(String sourceName) {
|
||||
String suffix = "." + THUMB_OUTPUT_FORMAT;
|
||||
String baseName = Network.simpleURIFileName(sourceName);
|
||||
int maxBaseLength = VARIANT_NAME_MAX_LENGTH - suffix.length();
|
||||
if (maxBaseLength < baseName.codePointCount(0, baseName.length())) {
|
||||
baseName = baseName.substring(0, baseName.offsetByCodePoints(0, maxBaseLength));
|
||||
}
|
||||
return baseName + suffix;
|
||||
}
|
||||
|
||||
private File createThumbFile(
|
||||
Attachment source,
|
||||
Integer requestWidth,
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ public class MailQueueServiceImplement extends AbstractQueueService<MailQueue, N
|
||||
message.setFrom(queue.getSendFrom());
|
||||
message.setTo(queue.getSendTo());
|
||||
message.setSubject(queue.getSubject());
|
||||
message.setText(queue.getData());
|
||||
message.setText(queue.getContent());
|
||||
mailSender.send(message);
|
||||
log.info("邮件发送成功,目标:{}", queue.getSendTo());
|
||||
}
|
||||
|
||||
+57
-10
@@ -1,10 +1,12 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.entity.Notify;
|
||||
import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
||||
import com.imyeyu.api.modules.common.mapper.NotifyDetailMapper;
|
||||
import com.imyeyu.api.modules.common.mapper.NotifyMapper;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.common.service.NotifyDetailService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
@@ -19,6 +21,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通知详情服务实现
|
||||
@@ -31,6 +34,7 @@ import java.util.List;
|
||||
public class NotifyDetailServiceImplement extends AbstractEntityService<NotifyDetail, String> implements NotifyDetailService {
|
||||
|
||||
private final NotifyMapper notifyMapper;
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
private final NotifyDetailMapper mapper;
|
||||
|
||||
@@ -53,15 +57,22 @@ public class NotifyDetailServiceImplement extends AbstractEntityService<NotifyDe
|
||||
if (detail.getMsgType() == NotifyDetail.MsgType.INTERNAL) {
|
||||
TimiException.required(detail.getSendTo(), "未找到站内通知接收用户 ID");
|
||||
}
|
||||
detail.setContentType(TimiJava.defaultIfNull(detail.getContentType(), detail.getMsgType() == NotifyDetail.MsgType.INTERNAL
|
||||
? NotifyDetail.ContentType.TIP_TAP
|
||||
: NotifyDetail.ContentType.TEXT));
|
||||
|
||||
detail.setRetry(TimiJava.defaultIfNull(detail.getRetry(), 1));
|
||||
detail.setSendAt(TimiJava.defaultIfNull(detail.getSendAt(), Time.now()));
|
||||
detail.setTimeoutAt(TimiJava.defaultIfNull(detail.getTimeoutAt(), detail.getSendAt() + Time.D));
|
||||
detail.setStatus(detail.getMsgType() == NotifyDetail.MsgType.INTERNAL
|
||||
? NotifyDetail.Status.REMINDED
|
||||
: NotifyDetail.Status.WAITING);
|
||||
boolean scheduled = Time.now() < detail.getSendAt();
|
||||
detail.setStatus(detail.getMsgType() == NotifyDetail.MsgType.INTERNAL && !scheduled
|
||||
? NotifyDetail.Status.REMINDED
|
||||
: NotifyDetail.Status.WAITING);
|
||||
detail.setReadAt(null);
|
||||
super.create(detail);
|
||||
if (detail.getMsgType() == NotifyDetail.MsgType.INTERNAL) {
|
||||
attachmentService.updateByBizId(Attachment.BizType.NOTIFY_DETAIL, detail.getId(), detail.getAttachmentList());
|
||||
}
|
||||
|
||||
if (detail.getMsgType().isQueue()) {
|
||||
// 加入外部消息队列
|
||||
@@ -77,7 +88,9 @@ public class NotifyDetailServiceImplement extends AbstractEntityService<NotifyDe
|
||||
public List<NotifyDetail> listByNotifyId(String notifyId) {
|
||||
NotifyDetail example = new NotifyDetail();
|
||||
example.setNotifyId(notifyId);
|
||||
return mapper.selectAllByExample(example);
|
||||
List<NotifyDetail> detailList = mapper.selectAllByExample(example);
|
||||
loadAttachments(detailList);
|
||||
return detailList;
|
||||
}
|
||||
|
||||
/// 删除通知详情
|
||||
@@ -135,9 +148,11 @@ public class NotifyDetailServiceImplement extends AbstractEntityService<NotifyDe
|
||||
result.setTotal(unreadOnly
|
||||
? mapper.countUnreadSiteByUserId(userId)
|
||||
: mapper.countSiteByUserId(userId));
|
||||
result.setList(unreadOnly
|
||||
List<NotifyDetail> detailList = unreadOnly
|
||||
? mapper.selectUnreadSiteByUserId(userId, validPage.getOffset(), validPage.getLimit())
|
||||
: mapper.selectSiteByUserId(userId, validPage.getOffset(), validPage.getLimit()));
|
||||
: mapper.selectSiteByUserId(userId, validPage.getOffset(), validPage.getLimit());
|
||||
loadAttachments(detailList);
|
||||
result.setList(detailList);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -152,19 +167,51 @@ public class NotifyDetailServiceImplement extends AbstractEntityService<NotifyDe
|
||||
TimiException.required(userId, "未找到用户 ID");
|
||||
NotifyDetail detail = mapper.selectSiteByIdAndUserId(detailId, userId);
|
||||
TimiException.required(detail, "未找到站内通知");
|
||||
loadAttachments(detail);
|
||||
return detail;
|
||||
}
|
||||
|
||||
/// 删除当前用户拥有的站内通知详情
|
||||
/// 回填通知详情的媒体附件
|
||||
///
|
||||
/// @param detailList 通知详情列表
|
||||
private void loadAttachments(List<NotifyDetail> detailList) {
|
||||
List<String> detailIdList = detailList.stream()
|
||||
.filter(detail -> detail.getMsgType() == NotifyDetail.MsgType.INTERNAL)
|
||||
.map(NotifyDetail::getId)
|
||||
.toList();
|
||||
if (detailIdList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
|
||||
Attachment.BizType.NOTIFY_DETAIL,
|
||||
detailIdList
|
||||
);
|
||||
for (NotifyDetail detail : detailList) {
|
||||
if (detail.getMsgType() == NotifyDetail.MsgType.INTERNAL) {
|
||||
detail.setAttachmentList(attachmentMap.getOrDefault(detail.getId(), List.of()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 回填通知详情的媒体附件
|
||||
///
|
||||
/// @param detail 通知详情
|
||||
private void loadAttachments(NotifyDetail detail) {
|
||||
if (detail.getMsgType() == NotifyDetail.MsgType.INTERNAL) {
|
||||
detail.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.NOTIFY_DETAIL, detail.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
/// 隐藏当前用户拥有的站内通知详情
|
||||
///
|
||||
/// @param detailId 通知详情 ID
|
||||
/// @param userId 用户 ID
|
||||
@Override
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
public void deleteInternalByUserId(String detailId, String userId) {
|
||||
public void hideInternalByUserId(String detailId, String userId) {
|
||||
TimiException.required(detailId, "未找到通知详情 ID");
|
||||
TimiException.required(userId, "未找到用户 ID");
|
||||
int affected = mapper.deleteSiteByIdAndUserId(detailId, userId);
|
||||
int affected = mapper.hideSiteByIdAndUserId(detailId, userId);
|
||||
TimiException.requiredTrue(0 < affected, "未找到站内通知");
|
||||
}
|
||||
|
||||
|
||||
+54
-4
@@ -8,10 +8,13 @@ import com.imyeyu.api.modules.common.service.NotifyDetailService;
|
||||
import com.imyeyu.api.modules.common.service.NotifyService;
|
||||
import com.imyeyu.api.modules.system.entity.Logger;
|
||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||
import com.imyeyu.api.modules.common.vo.NotifyManagementItem;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
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;
|
||||
@@ -53,12 +56,10 @@ public class NotifyServiceImplement extends AbstractEntityService<Notify, String
|
||||
try {
|
||||
TimiException.required(notify, "未找到通知");
|
||||
TimiException.required(notify.getDetailList(), "未找到通知详情列表");
|
||||
String sendFrom = userLoginService.isLogged() ? userLoginService.getRequireLoginUserId() : null;
|
||||
notify.setCreatedBy(sendFrom);
|
||||
super.create(notify);
|
||||
|
||||
String sendFrom = null;
|
||||
if (userLoginService.isLogged()) {
|
||||
sendFrom = userLoginService.getRequireLoginUserId();
|
||||
}
|
||||
for (NotifyDetail detail : notify.getDetailList()) {
|
||||
detail.setNotifyId(notify.getId());
|
||||
if (TimiJava.isNotEmpty(sendFrom)) {
|
||||
@@ -82,6 +83,55 @@ public class NotifyServiceImplement extends AbstractEntityService<Notify, String
|
||||
}
|
||||
}
|
||||
|
||||
/// 分页查询当前用户创建的通知
|
||||
///
|
||||
/// @param page 分页参数
|
||||
/// @param userId 用户 ID
|
||||
/// @return 管理端通知列表
|
||||
@Override
|
||||
public PageResult<NotifyManagementItem> pageManagement(Page<Notify> page, String userId) {
|
||||
TimiException.required(userId, "未找到用户 ID");
|
||||
Page<Notify> validPage = TimiJava.defaultIfNull(page, new Page<>());
|
||||
PageResult<NotifyManagementItem> result = new PageResult<>();
|
||||
result.setTotal(mapper.countManagement(userId));
|
||||
result.setList(mapper.selectManagementPage(userId, validPage.getOffset(), validPage.getLimit()));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 查询当前用户可管理的通知
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
/// @param userId 用户 ID
|
||||
/// @return 通知
|
||||
@Override
|
||||
public Notify getManagement(String notifyId, String userId) {
|
||||
TimiException.required(notifyId, "未找到通知 ID");
|
||||
TimiException.required(userId, "未找到用户 ID");
|
||||
Notify notify = mapper.selectManagement(notifyId, userId);
|
||||
TimiException.required(notify, "未找到可管理的通知");
|
||||
return notify;
|
||||
}
|
||||
|
||||
/// 删除当前用户创建的通知
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
/// @param userId 用户 ID
|
||||
@Override
|
||||
public void deleteManagement(String notifyId, String userId) {
|
||||
getManagement(notifyId, userId);
|
||||
delete(notifyId);
|
||||
}
|
||||
|
||||
/// 取消当前用户创建的通知
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
/// @param userId 用户 ID
|
||||
@Override
|
||||
public void cancelManagement(String notifyId, String userId) {
|
||||
getManagement(notifyId, userId);
|
||||
cancel(notifyId);
|
||||
}
|
||||
|
||||
/// 删除通知并级联软删除通知详情
|
||||
///
|
||||
/// @param notifyId 通知 ID
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.imyeyu.api.modules.common.vo;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
||||
import lombok.Data;
|
||||
|
||||
/// 管理端通知列表项
|
||||
///
|
||||
/// @author 夜雨
|
||||
/// @since 2026-08-25
|
||||
@Data
|
||||
public class NotifyManagementItem {
|
||||
|
||||
/// 通知 ID
|
||||
private String id;
|
||||
|
||||
/// 创建者用户 ID
|
||||
private String createdBy;
|
||||
|
||||
/// 创建时间
|
||||
private Long createdAt;
|
||||
|
||||
/// 更新时间
|
||||
private Long updatedAt;
|
||||
|
||||
/// 通知标题
|
||||
private String subject;
|
||||
|
||||
/// 通知内容
|
||||
private String content;
|
||||
|
||||
/// 内容类型
|
||||
private NotifyDetail.ContentType contentType;
|
||||
|
||||
/// 发送时间
|
||||
private Long sendAt;
|
||||
|
||||
/// 接收用户数量
|
||||
private Long recipientCount;
|
||||
|
||||
/// 已读用户数量
|
||||
private Long readCount;
|
||||
|
||||
/// 等待发送数量
|
||||
private Long waitingCount;
|
||||
|
||||
/// 已发送数量
|
||||
private Long remindedCount;
|
||||
|
||||
/// 超时数量
|
||||
private Long timeoutCount;
|
||||
|
||||
/// 已取消数量
|
||||
private Long cancelCount;
|
||||
|
||||
/// 失败数量
|
||||
private Long failCount;
|
||||
}
|
||||
@@ -21,6 +21,14 @@ public enum GaoPermissionCode implements BuiltinPermissionCode {
|
||||
|
||||
STORE_DELETE("STORE:DELETE", "门店删除", false, true),
|
||||
|
||||
STORE_DIARY_CREATE("STORE_DIARY:CREATE", "门店日记创建", true, false),
|
||||
|
||||
STORE_DIARY_READ("STORE_DIARY:READ", "门店日记读取", true, false),
|
||||
|
||||
STORE_DIARY_UPDATE("STORE_DIARY:UPDATE", "门店日记修改", true, false),
|
||||
|
||||
STORE_DIARY_DELETE("STORE_DIARY:DELETE", "门店日记删除", true, false),
|
||||
|
||||
CUSTOMER_CREATE("CUSTOMER:CREATE", "客户创建", false, false),
|
||||
|
||||
CUSTOMER_READ("CUSTOMER:READ", "客户读取", false, false),
|
||||
@@ -43,12 +51,48 @@ public enum GaoPermissionCode implements BuiltinPermissionCode {
|
||||
|
||||
EVENT_RECORD_READ("EVENT_RECORD:READ", "登记事件记录读取", false, false),
|
||||
|
||||
EVENT_RECORD_VERIFY("EVENT_RECORD:VERIFY", "登记事件核销", true, false),
|
||||
|
||||
EVENT_RECORD_EXPORT("EVENT_RECORD:EXPORT", "登记事件记录导出", true, false),
|
||||
|
||||
EVENT_RECORD_UPDATE("EVENT_RECORD:UPDATE", "登记事件记录修改", false, false),
|
||||
|
||||
EVENT_RECORD_DELETE("EVENT_RECORD:DELETE", "登记事件记录删除", false, false),
|
||||
|
||||
PACKAGE_CONFIG_CREATE("PACKAGE_CONFIG:CREATE", "套餐配置创建", true, true),
|
||||
|
||||
PACKAGE_CONFIG_READ("PACKAGE_CONFIG:READ", "套餐配置读取", true, true),
|
||||
|
||||
PACKAGE_CONFIG_UPDATE("PACKAGE_CONFIG:UPDATE", "套餐配置修改", true, true),
|
||||
|
||||
PACKAGE_CONFIG_DELETE("PACKAGE_CONFIG:DELETE", "套餐配置删除", true, true),
|
||||
|
||||
CUSTOMER_PACKAGE_CREATE("CUSTOMER_PACKAGE:CREATE", "客户套餐创建", true, true),
|
||||
|
||||
CUSTOMER_PACKAGE_READ("CUSTOMER_PACKAGE:READ", "客户套餐读取", true, true),
|
||||
|
||||
CUSTOMER_PACKAGE_CANCEL("CUSTOMER_PACKAGE:CANCEL", "客户套餐取消", true, true),
|
||||
|
||||
CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和权益配额调整", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_CREATE("PACKAGE_DELIVERY:CREATE", "套餐发放创建", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_READ("PACKAGE_DELIVERY:READ", "套餐发放读取", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_VERIFY("PACKAGE_DELIVERY:VERIFY", "套餐发放核销", true, true),
|
||||
|
||||
PACKAGE_DELIVERY_REVOKE("PACKAGE_DELIVERY:REVOKE", "套餐发放冲正", true, true),
|
||||
|
||||
FUND_RECORD_READ("FUND_RECORD:READ", "资金流水读取", true, true),
|
||||
|
||||
FUND_RECEIVE_CREATE("FUND_RECEIVE:CREATE", "线下收款创建", true, true),
|
||||
|
||||
FUND_RECEIVE_VERIFY("FUND_RECEIVE:VERIFY", "线下收款核销", true, true),
|
||||
|
||||
FUND_REFUND("FUND:REFUND", "套餐退款", true, true),
|
||||
|
||||
FUND_REVOKE("FUND:REVOKE", "资金流水冲正", true, true),
|
||||
|
||||
POINT_ACCOUNT_READ("POINT_ACCOUNT:READ", "客户积分账户读取", true, true),
|
||||
|
||||
POINT_LEDGER_READ("POINT_LEDGER:READ", "客户积分流水读取", true, true),
|
||||
|
||||
@@ -16,7 +16,7 @@ public enum GaoRoleCode implements BuiltinRoleCode {
|
||||
GLOBAL_MANAGER("门店管理", null),
|
||||
|
||||
/// 店长
|
||||
STORE_MANAGER("店长", GLOBAL_MANAGER.name());
|
||||
STORE_MANAGER("店长", null);
|
||||
|
||||
final String defaultName;
|
||||
final String parentCode;
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.imyeyu.api.modules.gao.controller;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerChartService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -26,6 +28,7 @@ import java.util.List;
|
||||
public class GaoCustomerChartController {
|
||||
|
||||
private final GaoCustomerChartService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
|
||||
/// 查询客户登记每日统计,可同时支撑登记趋势和登记事件结构图表
|
||||
@AOPLog
|
||||
@@ -33,6 +36,8 @@ public class GaoCustomerChartController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/event/record/daily")
|
||||
public List<GaoEventRecordDailyStatView> eventRecordDaily(@RequestBody GaoCustomerChartReq req) {
|
||||
TimiException.required(req, "not found req");
|
||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||
return service.eventRecordDailyStat(req);
|
||||
}
|
||||
|
||||
@@ -42,6 +47,8 @@ public class GaoCustomerChartController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/event/record/calendar")
|
||||
public List<GaoEventRecordCalendarView> eventRecordCalendar(@RequestBody GaoCustomerChartReq req) {
|
||||
TimiException.required(req, "not found req");
|
||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||
return service.eventRecordCalendar(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
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;
|
||||
@@ -50,11 +46,9 @@ import java.util.Map;
|
||||
public class GaoCustomerController {
|
||||
|
||||
private final UserService userService;
|
||||
private final RoleChecker roleChecker;
|
||||
private final AttachmentService attachmentService;
|
||||
private final GaoCustomerService service;
|
||||
private final GaoStoreService gaoStoreService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@@ -62,6 +56,8 @@ public class GaoCustomerController {
|
||||
@PostMapping("/list")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public PageResult<GaoCustomer> list(@RequestBody GaoCustomerPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoCustomer> result = service.pageByQuery(page);
|
||||
|
||||
List<String> idList = result.getList().stream().map(GaoCustomer::getId).distinct().toList();
|
||||
@@ -79,6 +75,8 @@ public class GaoCustomerController {
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoCustomer detail(@RequestParam String id) {
|
||||
GaoCustomer customer = service.get(id);
|
||||
TimiException.required(customer, "not found customer");
|
||||
storeScopeService.checkStoreId(customer.getStoreId());
|
||||
if (TimiJava.isNotEmpty(customer.getIntroducerCustomerId())) {
|
||||
GaoCustomer introducerCustomer = service.get(customer.getIntroducerCustomerId());
|
||||
introducerCustomer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, introducerCustomer.getId()));
|
||||
@@ -97,9 +95,7 @@ public class GaoCustomerController {
|
||||
@PostMapping("/create")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public String create(@RequestBody GaoCustomer customer) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
customer.setStoreId(gaoStoreService.getByUserId(userLoginService.getRequireLoginUserId()).getId());
|
||||
}
|
||||
customer.setStoreId(storeScopeService.resolveStoreId(customer.getStoreId()));
|
||||
service.create(customer);
|
||||
return customer.getId();
|
||||
}
|
||||
@@ -109,9 +105,12 @@ public class GaoCustomerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_UPDATE)
|
||||
@PostMapping("/update")
|
||||
public void update(@RequestBody GaoCustomer customer) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
customer.setStoreId(gaoStoreService.getByUserId(userLoginService.getRequireLoginUserId()).getId());
|
||||
}
|
||||
TimiException.required(customer, "not found customer");
|
||||
TimiException.required(customer.getId(), "not found customer.id");
|
||||
GaoCustomer dbCustomer = service.get(customer.getId());
|
||||
TimiException.required(dbCustomer, "not found customer");
|
||||
storeScopeService.checkStoreId(dbCustomer.getStoreId());
|
||||
customer.setStoreId(dbCustomer.getStoreId());
|
||||
service.updateWithAttachment(customer);
|
||||
}
|
||||
|
||||
@@ -120,6 +119,9 @@ public class GaoCustomerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_DELETE)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestParam String id) {
|
||||
GaoCustomer customer = service.get(id);
|
||||
TimiException.required(customer, "not found customer");
|
||||
storeScopeService.checkStoreId(customer.getStoreId());
|
||||
service.delete(id);
|
||||
}
|
||||
|
||||
@@ -131,6 +133,7 @@ public class GaoCustomerController {
|
||||
public GaoCustomer findById(@RequestParam String id) {
|
||||
GaoCustomer customer = service.get(id);
|
||||
TimiException.required(customer, "not found customer");
|
||||
storeScopeService.checkStoreId(customer.getStoreId());
|
||||
customer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, customer.getId()));
|
||||
return customer;
|
||||
}
|
||||
@@ -141,7 +144,7 @@ public class GaoCustomerController {
|
||||
@PostMapping("/find/code")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoCustomer findByCode(@RequestParam String storeId, @RequestParam String code) {
|
||||
GaoCustomer customer = service.getByCode(storeId, code);
|
||||
GaoCustomer customer = service.getByCode(storeScopeService.resolveStoreId(storeId), code);
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -155,7 +158,7 @@ public class GaoCustomerController {
|
||||
@PostMapping("/find/name")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoCustomer findByName(@RequestParam String storeId, @RequestParam String name) {
|
||||
GaoCustomer customer = service.getByName(storeId, name);
|
||||
GaoCustomer customer = service.getByName(storeScopeService.resolveStoreId(storeId), name);
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -169,6 +172,12 @@ public class GaoCustomerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(@RequestBody Page<GaoCustomer> page) throws IOException {
|
||||
TimiException.required(page, "not found page");
|
||||
if (!storeScopeService.hasGlobalScope()) {
|
||||
GaoCustomer example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoCustomer());
|
||||
example.setStoreId(storeScopeService.resolveStoreId(example.getStoreId()));
|
||||
page.setEqualsExample(example);
|
||||
}
|
||||
HttpServletResponse resp = TimiSpring.getResponse();
|
||||
String contentDisposition = Network.getFileDownloadHeader("客户列表-%s.xlsx".formatted(Time.serialize.format(Time.now())));
|
||||
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
@@ -182,6 +191,9 @@ public class GaoCustomerController {
|
||||
@PostMapping("/invited/list")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public List<GaoCustomer> listInvited(@RequestParam String introducerCustomerId) {
|
||||
GaoCustomer introducerCustomer = service.get(introducerCustomerId);
|
||||
TimiException.required(introducerCustomer, "not found introducerCustomer");
|
||||
storeScopeService.checkStoreId(introducerCustomer.getStoreId());
|
||||
List<GaoCustomer> result = service.listInvited(introducerCustomerId);
|
||||
List<String> idList = result.stream().map(GaoCustomer::getId).distinct().toList();
|
||||
Map<String, List<Attachment>> attachMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, idList);
|
||||
@@ -196,6 +208,9 @@ public class GaoCustomerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_UPDATE)
|
||||
@PostMapping("/invite/save")
|
||||
public void saveInvite(@RequestParam String introducerCustomerId, @RequestParam String customerId) {
|
||||
GaoCustomer customer = service.get(customerId);
|
||||
TimiException.required(customer, "not found customer");
|
||||
storeScopeService.checkStoreId(customer.getStoreId());
|
||||
service.saveInviteRelation(introducerCustomerId, customerId);
|
||||
}
|
||||
|
||||
@@ -204,6 +219,9 @@ public class GaoCustomerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_UPDATE)
|
||||
@PostMapping("/invite/delete")
|
||||
public void deleteInvite(@RequestParam String customerId) {
|
||||
GaoCustomer customer = service.get(customerId);
|
||||
TimiException.required(customer, "not found customer");
|
||||
storeScopeService.checkStoreId(customer.getStoreId());
|
||||
service.deleteInviteRelation(customerId);
|
||||
}
|
||||
|
||||
@@ -213,6 +231,8 @@ public class GaoCustomerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
|
||||
@PostMapping("/state/daily")
|
||||
public List<GaoCustomerDailyStateView> dailyStat(@RequestBody GaoCustomerTrendStateReq req) {
|
||||
TimiException.required(req, "not found req");
|
||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||
return service.stateDailyTrend(req);
|
||||
}
|
||||
|
||||
@@ -222,6 +242,6 @@ public class GaoCustomerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ)
|
||||
@PostMapping("/state/gender")
|
||||
public List<GaoCustomerGenderStatView> genderStat(@RequestParam String storeId) {
|
||||
return service.stateGender(storeId);
|
||||
return service.stateGender(storeScopeService.resolveStoreId(storeId));
|
||||
}
|
||||
}
|
||||
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDelivery;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDeliveryItem;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageDeliveryService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackageCancelRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundActionRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundConfirmNoRefundRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundReceiveRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRefundRequest;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.api.modules.user.service.UserService;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/// GAO 客户套餐和收退款接口
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/gao/customer/package")
|
||||
public class GaoCustomerPackageController {
|
||||
|
||||
private final GaoCustomerPackageService service;
|
||||
private final GaoCustomerService customerService;
|
||||
private final GaoEventRecordService eventRecordService;
|
||||
private final GaoPackageDeliveryService deliveryService;
|
||||
private final GaoFundRecordService fundRecordService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserService userService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_PACKAGE_READ)
|
||||
@PostMapping("/list")
|
||||
public PageResult<GaoCustomerPackage> list(@RequestBody GaoCustomerPackagePage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoCustomerPackage> result = service.pageByQuery(page);
|
||||
if (result.getList().isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
List<String> customerIdList = result.getList().stream()
|
||||
.map(GaoCustomerPackage::getCustomerId)
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, GaoCustomer> customerMap = customerService.mapByIdList(customerIdList);
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList, GaoCustomer.AttachType.PHOTO.name());
|
||||
for (GaoCustomerPackage packageEntity : result.getList()) {
|
||||
GaoCustomer customer = customerMap.get(packageEntity.getCustomerId());
|
||||
if (customer != null) {
|
||||
customer.setAttachmentList(attachmentMap.get(customer.getId()));
|
||||
}
|
||||
packageEntity.setCustomer(customer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_PACKAGE_READ)
|
||||
@PostMapping("/detail")
|
||||
public GaoCustomerPackage detail(@RequestParam String id) {
|
||||
GaoCustomerPackage result = service.get(id);
|
||||
TimiException.required(result, "not found customer package");
|
||||
storeScopeService.checkStoreId(result.getStoreId());
|
||||
fillTransient(List.of(result));
|
||||
GaoCustomer customer = customerService.get(result.getCustomerId());
|
||||
if (customer != null) {
|
||||
customer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, customer.getId()));
|
||||
}
|
||||
result.setCustomer(customer);
|
||||
GaoEventRecord purchaseRecord = eventRecordService.get(result.getPurchaseEventRecordId());
|
||||
if (purchaseRecord != null) {
|
||||
purchaseRecord.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_EVENT_RECORD, purchaseRecord.getId()));
|
||||
}
|
||||
result.setPurchaseEventRecord(purchaseRecord);
|
||||
result.setDeliveryList(deliveryService.listByCustomerPackageId(result.getId()));
|
||||
fillDeliveryTransient(result.getDeliveryList());
|
||||
result.setFundRecordList(fundRecordService.listByCustomerPackageId(result.getId()));
|
||||
fillFundTransient(result.getFundRecordList());
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_PACKAGE_CREATE)
|
||||
@PostMapping("/purchase")
|
||||
public GaoCustomerPackage purchase(@RequestBody GaoCustomerPackagePurchaseRequest request) {
|
||||
GaoCustomerPackage result = service.purchase(request);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.CUSTOMER_PACKAGE_CANCEL)
|
||||
@PostMapping("/cancel")
|
||||
public GaoCustomerPackage cancel(@RequestBody GaoCustomerPackageCancelRequest request) {
|
||||
GaoCustomerPackage result = service.cancel(request);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_RECEIVE_CREATE)
|
||||
@PostMapping("/payment/receive")
|
||||
public GaoCustomerPackage receive(@RequestBody GaoFundReceiveRequest request) {
|
||||
fundRecordService.receive(request);
|
||||
return packageResponse(request.getCustomerPackageId());
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_RECEIVE_VERIFY)
|
||||
@PostMapping("/payment/verify")
|
||||
public GaoCustomerPackage verifyPayment(@RequestParam String id) {
|
||||
fundRecordService.verify(id);
|
||||
return packageAfterFundAction(id);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_RECEIVE_VERIFY)
|
||||
@PostMapping("/payment/reject")
|
||||
public GaoCustomerPackage rejectPayment(@RequestBody GaoFundActionRequest request) {
|
||||
fundRecordService.reject(request);
|
||||
return packageAfterFundAction(request.getFundRecordId());
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_RECEIVE_CREATE)
|
||||
@PostMapping("/payment/cancel")
|
||||
public GaoCustomerPackage cancelPayment(@RequestBody GaoFundActionRequest request) {
|
||||
fundRecordService.cancel(request);
|
||||
return packageAfterFundAction(request.getFundRecordId());
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_REFUND)
|
||||
@PostMapping("/refund")
|
||||
public GaoCustomerPackage refund(@RequestBody GaoFundRefundRequest request) {
|
||||
fundRecordService.refund(request);
|
||||
return packageResponse(request.getCustomerPackageId());
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_REFUND)
|
||||
@PostMapping("/refund/confirm-none")
|
||||
public GaoCustomerPackage confirmNoRefund(@RequestBody GaoFundConfirmNoRefundRequest request) {
|
||||
fundRecordService.confirmNoRefund(request);
|
||||
return packageResponse(request.getCustomerPackageId());
|
||||
}
|
||||
|
||||
private GaoCustomerPackage packageAfterFundAction(String fundRecordId) {
|
||||
var record = fundRecordService.get(fundRecordId);
|
||||
TimiException.required(record, "not found fund record");
|
||||
return packageResponse(record.getBizId());
|
||||
}
|
||||
|
||||
private GaoCustomerPackage packageResponse(String packageId) {
|
||||
GaoCustomerPackage result = service.get(packageId);
|
||||
TimiException.required(result, "not found customer package");
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void fillTransient(List<GaoCustomerPackage> packageList) {
|
||||
if (packageList == null || packageList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<GaoCustomerPackageItem>> itemMap = service.mapItemListByCustomerPackageIdList(packageList.stream().map(GaoCustomerPackage::getId).toList());
|
||||
List<String> userIdList = packageList.stream()
|
||||
.flatMap(packageEntity -> Stream.of(packageEntity.getOperatorUserId(), packageEntity.getCancelOperatorUserId()))
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, User> userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList);
|
||||
for (GaoCustomerPackage packageEntity : packageList) {
|
||||
List<GaoCustomerPackageItem> itemList = itemMap.getOrDefault(packageEntity.getId(), List.of());
|
||||
for (GaoCustomerPackageItem item : itemList) {
|
||||
fillDerived(item);
|
||||
}
|
||||
packageEntity.setItemList(itemList);
|
||||
packageEntity.setOperator(userMap.get(packageEntity.getOperatorUserId()));
|
||||
packageEntity.setCancelOperator(userMap.get(packageEntity.getCancelOperatorUserId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDeliveryTransient(List<GaoPackageDelivery> deliveryList) {
|
||||
if (deliveryList == null || deliveryList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> idList = deliveryList.stream().map(GaoPackageDelivery::getId).toList();
|
||||
Map<String, List<GaoPackageDeliveryItem>> itemMap = deliveryService.mapItemListByDeliveryIdList(idList);
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_PACKAGE_DELIVERY, idList);
|
||||
List<String> userIdList = deliveryList.stream()
|
||||
.flatMap(delivery -> Stream.of(delivery.getOperatorUserId(), delivery.getVerifierUserId()))
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, User> userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList);
|
||||
for (GaoPackageDelivery delivery : deliveryList) {
|
||||
delivery.setItemList(itemMap.getOrDefault(delivery.getId(), List.of()));
|
||||
delivery.setAttachmentList(attachmentMap.getOrDefault(delivery.getId(), List.of()));
|
||||
delivery.setOperator(userMap.get(delivery.getOperatorUserId()));
|
||||
delivery.setVerifier(userMap.get(delivery.getVerifierUserId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillFundTransient(List<GaoFundRecord> recordList) {
|
||||
if (recordList == null || recordList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> idList = recordList.stream().map(GaoFundRecord::getId).toList();
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_FUND_RECORD, idList);
|
||||
List<String> userIdList = recordList.stream()
|
||||
.flatMap(record -> Stream.of(record.getOperatorUserId(), record.getVerifierUserId()))
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, User> userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList);
|
||||
for (GaoFundRecord record : recordList) {
|
||||
record.setAttachmentList(attachmentMap.getOrDefault(record.getId(), List.of()));
|
||||
record.setOperator(userMap.get(record.getOperatorUserId()));
|
||||
record.setVerifier(userMap.get(record.getVerifierUserId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDerived(GaoCustomerPackageItem item) {
|
||||
BigDecimal reserved = item.getReservedQuantity() == null ? BigDecimal.ZERO : item.getReservedQuantity();
|
||||
BigDecimal delivered = item.getDeliveredQuantity() == null ? BigDecimal.ZERO : item.getDeliveredQuantity();
|
||||
BigDecimal total = item.getTotalQuantity() == null ? BigDecimal.ZERO : item.getTotalQuantity();
|
||||
item.setPendingQuantity(reserved);
|
||||
item.setAvailableQuantity(total.subtract(reserved).subtract(delivered));
|
||||
item.setRemainingQuantity(total.subtract(delivered));
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOptionGroup;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
@@ -33,28 +35,76 @@ import java.util.Map;
|
||||
public class GaoEventController {
|
||||
|
||||
private final GaoEventService service;
|
||||
private final RoleChecker roleChecker;
|
||||
private final GaoStoreService gaoStoreService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final GaoPackageConfigService packageConfigService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_READ)
|
||||
@PostMapping("/list")
|
||||
public PageResult<GaoEvent> list(@RequestBody Page<GaoEvent> page) {
|
||||
PageResult<GaoEvent> result = service.page(page);
|
||||
Map<String, List<String>> roleMap = service.mapRoleCodeListByEventIdList(result.getList().stream().map(GaoEvent::getId).toList());
|
||||
for (GaoEvent event : result.getList()) {
|
||||
event.setRoleCodeList(roleMap.get(event.getId()));
|
||||
}
|
||||
return result;
|
||||
TimiException.required(page, "not found page");
|
||||
resolveStoreScope(page);
|
||||
PageResult<GaoEvent> result = service.pageWithoutDeleted(page);
|
||||
return attachEventRelations(result);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_CREATE)
|
||||
@PostMapping("/list/history")
|
||||
public PageResult<GaoEvent> listHistory(@RequestBody Page<GaoEvent> page) {
|
||||
TimiException.required(page, "not found page");
|
||||
resolveStoreScope(page);
|
||||
return attachEventRelations(service.page(page));
|
||||
}
|
||||
|
||||
private void resolveStoreScope(Page<GaoEvent> page) {
|
||||
if (!storeScopeService.hasGlobalScope()) {
|
||||
GaoEvent example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoEvent());
|
||||
example.setStoreId(storeScopeService.resolveStoreId(example.getStoreId()));
|
||||
page.setEqualsExample(example);
|
||||
}
|
||||
}
|
||||
|
||||
private PageResult<GaoEvent> attachEventRelations(PageResult<GaoEvent> result) {
|
||||
Map<String, List<String>> roleMap = service.mapRoleCodeListByEventIdList(result.getList().stream().map(GaoEvent::getId).toList());
|
||||
for (GaoEvent event : result.getList()) {
|
||||
event.setRoleCodeList(roleMap.get(event.getId()));
|
||||
}
|
||||
service.attachOptionGroupList(result.getList());
|
||||
attachPackageConfig(result.getList());
|
||||
return result;
|
||||
}
|
||||
|
||||
private void attachPackageConfig(List<GaoEvent> eventList) {
|
||||
List<String> packageConfigIdList = eventList.stream()
|
||||
.map(GaoEvent::getPackageConfigId)
|
||||
.filter(TimiJava::isNotEmpty)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (packageConfigIdList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, GaoPackageConfig> packageConfigMap = packageConfigService.mapByIdList(packageConfigIdList);
|
||||
Map<String, List<GaoPackageConfigItem>> itemMap = packageConfigService.mapItemListByPackageConfigIdList(packageConfigIdList);
|
||||
for (GaoEvent event : eventList) {
|
||||
GaoPackageConfig packageConfig = packageConfigMap.get(event.getPackageConfigId());
|
||||
if (packageConfig != null) {
|
||||
packageConfig.setItemList(itemMap.getOrDefault(packageConfig.getId(), List.of()));
|
||||
event.setPackageConfig(packageConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_READ)
|
||||
@PostMapping("/list/valid")
|
||||
public List<GaoEvent> listValid(@RequestParam String storeId) {
|
||||
return service.listValid(storeId);
|
||||
List<GaoEvent> result = service.listValid(storeScopeService.resolveStoreId(storeId));
|
||||
attachPackageConfig(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@@ -63,7 +113,11 @@ public class GaoEventController {
|
||||
@PostMapping("/detail")
|
||||
public GaoEvent detail(@RequestParam String id) {
|
||||
GaoEvent event = service.get(id);
|
||||
TimiException.required(event, "not found event");
|
||||
storeScopeService.checkStoreId(event.getStoreId());
|
||||
event.setRoleCodeList(service.listRoleCode(event.getId()));
|
||||
service.attachOptionGroupList(List.of(event));
|
||||
attachPackageConfig(List.of(event));
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -72,9 +126,7 @@ public class GaoEventController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_CREATE)
|
||||
@PostMapping("/create")
|
||||
public void create(@RequestBody GaoEvent event) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
event.setStoreId(gaoStoreService.getByUserId(userLoginService.getRequireLoginUserId()).getId());
|
||||
}
|
||||
event.setStoreId(storeScopeService.resolveStoreId(event.getStoreId()));
|
||||
service.create(event);
|
||||
}
|
||||
|
||||
@@ -83,17 +135,36 @@ public class GaoEventController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_UPDATE)
|
||||
@PostMapping("/update")
|
||||
public void update(@RequestBody GaoEvent event) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
event.setStoreId(gaoStoreService.getByUserId(userLoginService.getRequireLoginUserId()).getId());
|
||||
}
|
||||
TimiException.required(event, "not found event");
|
||||
TimiException.required(event.getId(), "not found event.id");
|
||||
GaoEvent dbEvent = service.get(event.getId());
|
||||
TimiException.required(dbEvent, "not found event");
|
||||
storeScopeService.checkStoreId(dbEvent.getStoreId());
|
||||
event.setStoreId(dbEvent.getStoreId());
|
||||
service.update(event);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_UPDATE)
|
||||
@PostMapping("/options/update")
|
||||
public void updateOptions(@RequestParam String id, @RequestBody List<GaoEventOptionGroup> optionGroupList) {
|
||||
GaoEvent event = service.get(id);
|
||||
TimiException.required(event, "not found event");
|
||||
storeScopeService.checkStoreId(event.getStoreId());
|
||||
service.updateOptionGroupList(id, optionGroupList);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_UPDATE)
|
||||
@PostMapping("/sort")
|
||||
public void sort(@RequestBody List<String> idList) {
|
||||
for (String id : TimiJava.safeIterable(idList)) {
|
||||
GaoEvent event = service.get(id);
|
||||
TimiException.required(event, "not found event");
|
||||
storeScopeService.checkStoreId(event.getStoreId());
|
||||
}
|
||||
service.sort(idList);
|
||||
}
|
||||
|
||||
@@ -102,6 +173,9 @@ public class GaoEventController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_DELETE)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestParam String id) {
|
||||
GaoEvent event = service.get(id);
|
||||
TimiException.required(event, "not found event");
|
||||
storeScopeService.checkStoreId(event.getStoreId());
|
||||
service.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventOptionStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordPageResult;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
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.network.Network;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
@@ -52,13 +50,11 @@ import java.util.Map;
|
||||
public class GaoEventRecordController {
|
||||
|
||||
private final UserService userService;
|
||||
private final RoleChecker roleChecker;
|
||||
private final GaoEventService eventService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final GaoCustomerService customerService;
|
||||
private final GaoStoreService gaoStoreService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final GaoEventRecordService service;
|
||||
private final UserLoginService userLoginService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@@ -66,6 +62,8 @@ public class GaoEventRecordController {
|
||||
@PostMapping("/list")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoEventRecordPageResult list(@RequestBody GaoEventRecordPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoEventRecord> pageResult = service.pageByRange(page);
|
||||
GaoEventRecordPageResult result = new GaoEventRecordPageResult();
|
||||
result.setTotal(pageResult.getTotal());
|
||||
@@ -81,6 +79,7 @@ public class GaoEventRecordController {
|
||||
record.setCustomer(customerMap.get(record.getCustomerId()));
|
||||
record.getCustomer().setAttachmentList(attachMap.get(record.getCustomerId()));
|
||||
}
|
||||
service.attachSelectedOptionGroupList(result.getList());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -108,22 +107,39 @@ public class GaoEventRecordController {
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoEventRecord detail(@RequestParam String id) {
|
||||
GaoEventRecord record = service.get(id);
|
||||
TimiException.required(record, "not found event record");
|
||||
storeScopeService.checkStoreId(record.getStoreId());
|
||||
record.setCustomer(customerService.get(record.getCustomerId()));
|
||||
record.getCustomer().setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, record.getCustomerId()));
|
||||
record.setEvent(eventService.get(record.getEventId()));
|
||||
eventService.attachOptionGroupList(List.of(record.getEvent()));
|
||||
record.setOperator(userService.get(record.getOperatorUserId()));
|
||||
record.setUpdater(userService.get(record.getUpdatedBy()));
|
||||
if (record.getVerifierUserId() != null) {
|
||||
record.setVerifier(userService.get(record.getVerifierUserId()));
|
||||
}
|
||||
record.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_EVENT_RECORD, record.getId()));
|
||||
service.attachSelectedOptionGroupList(List.of(record));
|
||||
return record;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_VERIFY)
|
||||
@PostMapping("/verify")
|
||||
public void verify(@RequestParam String id) {
|
||||
GaoEventRecord record = service.get(id);
|
||||
TimiException.required(record, "not found event record");
|
||||
storeScopeService.checkStoreId(record.getStoreId());
|
||||
service.verify(id);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_CREATE)
|
||||
@PostMapping("/create")
|
||||
public void create(@RequestBody GaoEventRecord record) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
record.setStoreId(gaoStoreService.getByUserId(userLoginService.getRequireLoginUserId()).getId());
|
||||
}
|
||||
resolveCreateRecordStore(record);
|
||||
service.create(record);
|
||||
}
|
||||
|
||||
@@ -132,11 +148,8 @@ public class GaoEventRecordController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_CREATE)
|
||||
@PostMapping("/create/batch")
|
||||
public void createBatch(@RequestBody List<GaoEventRecord> recordList) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
String storeId = gaoStoreService.getBelongIdByRequiredLoginUserId();
|
||||
for (GaoEventRecord record : recordList) {
|
||||
record.setStoreId(storeId);
|
||||
}
|
||||
for (GaoEventRecord record : TimiJava.safeIterable(recordList)) {
|
||||
resolveCreateRecordStore(record);
|
||||
}
|
||||
service.createBatch(recordList);
|
||||
}
|
||||
@@ -146,11 +159,17 @@ public class GaoEventRecordController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_CREATE)
|
||||
@PostMapping("/create/customer")
|
||||
public void createWithCustomer(@RequestBody GaoCustomer customer) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
String storeId = gaoStoreService.getBelongIdByRequiredLoginUserId();
|
||||
for (GaoEventRecord record : TimiJava.safeIterable(customer.getEventRecordList())) {
|
||||
record.setStoreId(storeId);
|
||||
}
|
||||
TimiException.required(customer, "not found customer");
|
||||
if (TimiJava.isNotEmpty(customer.getId())) {
|
||||
GaoCustomer dbCustomer = customerService.get(customer.getId());
|
||||
TimiException.required(dbCustomer, "not found customer");
|
||||
storeScopeService.checkStoreId(dbCustomer.getStoreId());
|
||||
customer.setStoreId(dbCustomer.getStoreId());
|
||||
} else {
|
||||
customer.setStoreId(storeScopeService.resolveStoreId(customer.getStoreId()));
|
||||
}
|
||||
for (GaoEventRecord record : TimiJava.safeIterable(customer.getEventRecordList())) {
|
||||
resolveCreateRecordStore(record);
|
||||
}
|
||||
service.createWithCustomer(customer);
|
||||
}
|
||||
@@ -160,9 +179,12 @@ public class GaoEventRecordController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_UPDATE)
|
||||
@PostMapping("/update")
|
||||
public void update(@RequestBody GaoEventRecord record) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
record.setStoreId(gaoStoreService.getBelongIdByRequiredLoginUserId());
|
||||
}
|
||||
TimiException.required(record, "not found event record");
|
||||
TimiException.required(record.getId(), "not found event record.id");
|
||||
GaoEventRecord dbRecord = service.get(record.getId());
|
||||
TimiException.required(dbRecord, "not found event record");
|
||||
storeScopeService.checkStoreId(dbRecord.getStoreId());
|
||||
record.setStoreId(dbRecord.getStoreId());
|
||||
service.update(record);
|
||||
}
|
||||
|
||||
@@ -171,6 +193,9 @@ public class GaoEventRecordController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_DELETE)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestParam String id) {
|
||||
GaoEventRecord record = service.get(id);
|
||||
TimiException.required(record, "not found event record");
|
||||
storeScopeService.checkStoreId(record.getStoreId());
|
||||
service.delete(id);
|
||||
}
|
||||
|
||||
@@ -180,6 +205,8 @@ public class GaoEventRecordController {
|
||||
@RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(@RequestBody GaoEventRecordPage page) throws IOException {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
HttpServletResponse resp = TimiSpring.getResponse();
|
||||
String contentDisposition = Network.getFileDownloadHeader("登记事件记录-%s.xlsx".formatted(Time.serialize.format(Time.now())));
|
||||
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
@@ -193,6 +220,8 @@ public class GaoEventRecordController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/rank")
|
||||
public PageResult<GaoCustomerEventRankView> eventRank(@RequestBody GaoEventRecordRankPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoCustomerEventRankView> result = service.pageRankByRange(page);
|
||||
List<String> customerIdList = result.getList().stream().map(GaoCustomerEventRankView::getCustomerId).distinct().toList();
|
||||
if (customerIdList.isEmpty()) {
|
||||
@@ -209,4 +238,22 @@ public class GaoEventRecordController {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/option/stat")
|
||||
public List<GaoEventOptionStatView> optionStat(@RequestBody GaoEventRecordPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
return service.optionStatByRange(page);
|
||||
}
|
||||
|
||||
private void resolveCreateRecordStore(GaoEventRecord record) {
|
||||
TimiException.required(record, "not found event record");
|
||||
GaoEvent event = eventService.get(record.getEventId());
|
||||
TimiException.required(event, "not found event");
|
||||
storeScopeService.checkStoreId(event.getStoreId());
|
||||
record.setStoreId(event.getStoreId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||
import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRecordPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRevokeRequest;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.api.modules.user.service.UserService;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/// GAO 资金流水接口
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/gao/fund/record")
|
||||
public class GaoFundRecordController {
|
||||
|
||||
private final GaoFundRecordService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final UserService userService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_RECORD_READ)
|
||||
@PostMapping("/list")
|
||||
public PageResult<GaoFundRecord> list(@RequestBody GaoFundRecordPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoFundRecord> result = service.pageByQuery(page);
|
||||
fillTransient(result.getList());
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_RECORD_READ)
|
||||
@PostMapping("/detail")
|
||||
public GaoFundRecord detail(@RequestParam String id) {
|
||||
GaoFundRecord result = service.get(id);
|
||||
TimiException.required(result, "not found fund record");
|
||||
storeScopeService.checkStoreId(result.getStoreId());
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.FUND_REVOKE)
|
||||
@PostMapping("/revoke")
|
||||
public GaoFundRecord revoke(@RequestBody GaoFundRevokeRequest request) {
|
||||
GaoFundRecord result = service.revoke(request);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void fillTransient(List<GaoFundRecord> recordList) {
|
||||
if (recordList == null || recordList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> idList = recordList.stream().map(GaoFundRecord::getId).toList();
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_FUND_RECORD, idList);
|
||||
List<String> userIdList = recordList.stream()
|
||||
.flatMap(record -> Stream.of(record.getOperatorUserId(), record.getVerifierUserId()))
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, User> userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList);
|
||||
for (GaoFundRecord record : recordList) {
|
||||
record.setAttachmentList(attachmentMap.getOrDefault(record.getId(), List.of()));
|
||||
record.setOperator(userMap.get(record.getOperatorUserId()));
|
||||
record.setVerifier(userMap.get(record.getVerifierUserId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.imyeyu.api.annotation.RequireCorePermission;
|
||||
import com.imyeyu.api.bean.CorePermissionCode;
|
||||
import com.imyeyu.api.bean.PermissionMatchMode;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.entity.Notify;
|
||||
import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.common.service.NotifyService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoUser;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoUserService;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
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.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.annotation.RequiredToken;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.util.ResponseView;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// GAO 通知接口
|
||||
///
|
||||
/// 这里的通知接收用户必须属于当前操作人的 GAO 用户范围,当前登录用户本人除外。
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-25
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/gao/notify")
|
||||
public class GaoNotifyController {
|
||||
|
||||
private final GaoUserService gaoUserService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final NotifyService notifyService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final UserService userService;
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
/// 查询可作为通知接收人的 GAO 用户
|
||||
///
|
||||
/// 门店管理范围可以查询全部门店或指定门店,店长只能查询当前门店。
|
||||
///
|
||||
/// @param page 分页参数
|
||||
/// @return 可发送用户分页结果
|
||||
@JsonView(ResponseView.Public.class)
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@RequireCorePermission(CorePermissionCode.NOTIFY_CREATE)
|
||||
@RequireGaoPermission(value = {GaoPermissionCode.USER_READ, GaoPermissionCode.STORE_READ}, mode = PermissionMatchMode.ANY)
|
||||
@PostMapping("/recipient/list")
|
||||
public PageResult<GaoUser> recipientList(@RequestBody Page<GaoUser> page) {
|
||||
TimiException.required(page, "未找到分页参数");
|
||||
GaoUser example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoUser());
|
||||
if (storeScopeService.hasGlobalScope()) {
|
||||
if (TimiJava.isNotEmpty(example.getStoreId())) {
|
||||
storeScopeService.checkStoreId(example.getStoreId());
|
||||
}
|
||||
} else {
|
||||
example.setStoreId(storeScopeService.resolveStoreId(example.getStoreId()));
|
||||
}
|
||||
page.setEqualsExample(example);
|
||||
|
||||
PageResult<GaoUser> result = gaoUserService.pageRecipients(page);
|
||||
fillUser(result.getList());
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 创建 GAO 站内通知
|
||||
///
|
||||
/// 只允许发送站内通知,并逐个校验接收用户是否属于当前操作人的门店范围;当前用户可以接收自己发送的通知。
|
||||
///
|
||||
/// @param notify 通知及通知详情
|
||||
/// @return 创建后的通知
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@RequireCorePermission(CorePermissionCode.NOTIFY_CREATE)
|
||||
@RequireGaoPermission(value = {GaoPermissionCode.USER_READ, GaoPermissionCode.STORE_READ}, mode = PermissionMatchMode.ANY)
|
||||
@PostMapping("/create")
|
||||
public Notify create(@RequestBody Notify notify) {
|
||||
checkRecipientBoundary(notify);
|
||||
notifyService.create(notify);
|
||||
return notify;
|
||||
}
|
||||
|
||||
/// 校验通知接收用户边界
|
||||
///
|
||||
/// @param notify 通知
|
||||
private void checkRecipientBoundary(Notify notify) {
|
||||
TimiException.required(notify, "未找到通知");
|
||||
List<NotifyDetail> detailList = notify.getDetailList();
|
||||
TimiException.required(detailList, "未找到通知详情列表");
|
||||
TimiException.requiredTrue(!detailList.isEmpty(), "通知详情列表不能为空");
|
||||
|
||||
Set<String> recipientIdSet = detailList.stream().map(detail -> {
|
||||
TimiException.required(detail, "未找到通知详情");
|
||||
TimiException.requiredTrue(detail.getMsgType() == NotifyDetail.MsgType.INTERNAL, "GAO 通知仅支持站内通知");
|
||||
TimiException.required(detail.getSendTo(), "未找到通知接收用户 ID");
|
||||
if (detail.getSendAt() != null) {
|
||||
TimiException.requiredTrue(Time.now() < detail.getSendAt(), "定时发送时间必须晚于当前时间");
|
||||
}
|
||||
return detail.getSendTo();
|
||||
}).collect(Collectors.toSet());
|
||||
TimiException.requiredTrue(recipientIdSet.size() == detailList.size(), "通知接收用户不能重复");
|
||||
|
||||
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||
Set<String> gaoRecipientIdSet = recipientIdSet.stream()
|
||||
.filter(recipientId -> !loginUserId.equals(recipientId))
|
||||
.collect(Collectors.toSet());
|
||||
Map<String, GaoUser> gaoUserMap = gaoUserService.mapByUserIdList(gaoRecipientIdSet);
|
||||
TimiException.requiredTrue(gaoUserMap.size() == gaoRecipientIdSet.size(), "包含无效的通知接收用户");
|
||||
for (GaoUser gaoUser : gaoUserMap.values()) {
|
||||
TimiException.requiredTrue(Boolean.TRUE.equals(gaoUser.getEnabled()), "通知接收用户已停用");
|
||||
storeScopeService.checkStoreId(gaoUser.getStoreId());
|
||||
}
|
||||
}
|
||||
|
||||
/// 回填 GAO 用户的核心用户资料
|
||||
///
|
||||
/// @param gaoUserList GAO 用户列表
|
||||
private void fillUser(List<GaoUser> gaoUserList) {
|
||||
if (TimiJava.isEmpty(gaoUserList)) {
|
||||
return;
|
||||
}
|
||||
List<String> userIdList = gaoUserList.stream().map(GaoUser::getUserId).toList();
|
||||
Map<String, User> userMap = userService.mapByIdList(userIdList);
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
|
||||
Attachment.BizType.USER,
|
||||
userIdList,
|
||||
User.AttachType.AVATAR.toString()
|
||||
);
|
||||
for (GaoUser gaoUser : gaoUserList) {
|
||||
User user = userMap.get(gaoUser.getUserId());
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
user.setAttachmentList(attachmentMap.getOrDefault(user.getId(), List.of()));
|
||||
gaoUser.setUser(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.api.modules.user.service.UserService;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/// GAO 套餐配置接口
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/gao/package/config")
|
||||
public class GaoPackageConfigController {
|
||||
|
||||
private final GaoPackageConfigService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserService userService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_CONFIG_READ)
|
||||
@PostMapping("/list")
|
||||
public PageResult<GaoPackageConfig> list(@RequestBody GaoPackageConfigPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoPackageConfig> result = service.pageByQuery(page);
|
||||
fillTransient(result.getList());
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_CONFIG_READ)
|
||||
@PostMapping("/list/valid")
|
||||
public List<GaoPackageConfig> listValid(@RequestParam String storeId) {
|
||||
List<GaoPackageConfig> result = service.listValid(storeScopeService.resolveStoreId(storeId));
|
||||
fillTransient(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_CONFIG_READ)
|
||||
@PostMapping("/detail")
|
||||
public GaoPackageConfig detail(@RequestParam String id) {
|
||||
GaoPackageConfig config = service.get(id);
|
||||
TimiException.required(config, "not found package config");
|
||||
storeScopeService.checkStoreId(config.getStoreId());
|
||||
fillTransient(List.of(config));
|
||||
return config;
|
||||
}
|
||||
|
||||
private void fillTransient(List<GaoPackageConfig> configList) {
|
||||
if (configList == null || configList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<GaoPackageConfigItem>> itemMap = service.mapItemListByPackageConfigIdList(configList.stream().map(GaoPackageConfig::getId).toList());
|
||||
List<String> updaterIdList = configList.stream().map(GaoPackageConfig::getUpdatedBy).filter(value -> value != null && !value.isBlank()).distinct().toList();
|
||||
Map<String, User> updaterMap = updaterIdList.isEmpty() ? Map.of() : userService.mapByIdList(updaterIdList);
|
||||
for (GaoPackageConfig config : configList) {
|
||||
config.setItemList(itemMap.getOrDefault(config.getId(), List.of()));
|
||||
config.setUpdater(updaterMap.get(config.getUpdatedBy()));
|
||||
}
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_CONFIG_CREATE)
|
||||
@PostMapping("/create")
|
||||
public String create(@RequestBody GaoPackageConfig config) {
|
||||
TimiException.required(config, "not found package config");
|
||||
config.setStoreId(storeScopeService.resolveStoreId(config.getStoreId()));
|
||||
service.create(config);
|
||||
return config.getId();
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_CONFIG_UPDATE)
|
||||
@PostMapping("/update")
|
||||
public void update(@RequestBody GaoPackageConfig config) {
|
||||
TimiException.required(config, "not found package config");
|
||||
TimiException.required(config.getId(), "not found packageConfig.id");
|
||||
GaoPackageConfig dbConfig = service.get(config.getId());
|
||||
TimiException.required(dbConfig, "not found package config");
|
||||
storeScopeService.checkStoreId(dbConfig.getStoreId());
|
||||
config.setStoreId(dbConfig.getStoreId());
|
||||
service.update(config);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_CONFIG_DELETE)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestParam String id) {
|
||||
GaoPackageConfig config = service.get(id);
|
||||
TimiException.required(config, "not found package config");
|
||||
storeScopeService.checkStoreId(config.getStoreId());
|
||||
service.delete(id);
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDelivery;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDeliveryItem;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageDeliveryService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryActionRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryCreateRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryRevokeRequest;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.api.modules.user.service.UserService;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/// GAO 套餐发放接口
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/gao/package/delivery")
|
||||
public class GaoPackageDeliveryController {
|
||||
|
||||
private final GaoPackageDeliveryService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final UserService userService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_DELIVERY_READ)
|
||||
@PostMapping("/list")
|
||||
public PageResult<GaoPackageDelivery> list(@RequestBody GaoPackageDeliveryPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<GaoPackageDelivery> result = service.pageByQuery(page);
|
||||
fillTransient(result.getList());
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_DELIVERY_READ)
|
||||
@PostMapping("/detail")
|
||||
public GaoPackageDelivery detail(@RequestParam String id) {
|
||||
GaoPackageDelivery result = service.get(id);
|
||||
TimiException.required(result, "not found delivery");
|
||||
storeScopeService.checkStoreId(result.getStoreId());
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_DELIVERY_CREATE)
|
||||
@PostMapping("/create")
|
||||
public GaoPackageDelivery create(@RequestBody GaoPackageDeliveryCreateRequest request) {
|
||||
GaoPackageDelivery result = service.createDelivery(request);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_DELIVERY_VERIFY)
|
||||
@PostMapping("/verify")
|
||||
public GaoPackageDelivery verify(@RequestParam String id) {
|
||||
GaoPackageDelivery result = service.verify(id);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_DELIVERY_VERIFY)
|
||||
@PostMapping("/reject")
|
||||
public GaoPackageDelivery reject(@RequestBody GaoPackageDeliveryActionRequest request) {
|
||||
GaoPackageDelivery result = service.reject(request);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_DELIVERY_CREATE)
|
||||
@PostMapping("/cancel")
|
||||
public GaoPackageDelivery cancel(@RequestBody GaoPackageDeliveryActionRequest request) {
|
||||
GaoPackageDelivery result = service.cancel(request);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.PACKAGE_DELIVERY_REVOKE)
|
||||
@PostMapping("/revoke")
|
||||
public GaoPackageDelivery revoke(@RequestBody GaoPackageDeliveryRevokeRequest request) {
|
||||
GaoPackageDelivery result = service.revoke(request);
|
||||
fillTransient(List.of(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void fillTransient(List<GaoPackageDelivery> deliveryList) {
|
||||
if (deliveryList == null || deliveryList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> idList = deliveryList.stream().map(GaoPackageDelivery::getId).toList();
|
||||
Map<String, List<GaoPackageDeliveryItem>> itemMap = service.mapItemListByDeliveryIdList(idList);
|
||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_PACKAGE_DELIVERY, idList);
|
||||
List<String> userIdList = deliveryList.stream()
|
||||
.flatMap(delivery -> Stream.of(delivery.getOperatorUserId(), delivery.getVerifierUserId()))
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, User> userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList);
|
||||
for (GaoPackageDelivery delivery : deliveryList) {
|
||||
delivery.setItemList(itemMap.getOrDefault(delivery.getId(), List.of()));
|
||||
delivery.setAttachmentList(attachmentMap.getOrDefault(delivery.getId(), List.of()));
|
||||
delivery.setOperator(userMap.get(delivery.getOperatorUserId()));
|
||||
delivery.setVerifier(userMap.get(delivery.getVerifierUserId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPointAccountPage;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
@@ -29,8 +26,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/gao/customer/point")
|
||||
public class GaoPointAccountController {
|
||||
|
||||
private final RoleChecker roleChecker;
|
||||
private final GaoStoreService storeService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final GaoPointAccountService accountService;
|
||||
|
||||
@AOPLog
|
||||
@@ -39,7 +35,7 @@ public class GaoPointAccountController {
|
||||
@PostMapping("/account/list")
|
||||
public PageResult<GaoPointAccount> list(@RequestBody GaoPointAccountPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(resolveStoreId(page.getStoreId()));
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
return accountService.pageByQuery(page);
|
||||
}
|
||||
|
||||
@@ -48,7 +44,7 @@ public class GaoPointAccountController {
|
||||
@RequireGaoPermission(GaoPermissionCode.POINT_ACCOUNT_READ)
|
||||
@PostMapping("/account/detail/customer")
|
||||
public GaoPointAccount detail(@RequestParam String customerId, @RequestParam(required = false) String storeId) {
|
||||
storeId = resolveStoreId(storeId);
|
||||
storeId = storeScopeService.resolveStoreId(storeId);
|
||||
return accountService.getByCustomerId(storeId, customerId);
|
||||
}
|
||||
|
||||
@@ -57,13 +53,6 @@ public class GaoPointAccountController {
|
||||
@RequireGaoPermission(GaoPermissionCode.POINT_ACCOUNT_READ)
|
||||
@PostMapping("/account/detail")
|
||||
public GaoPointAccount detailById(@RequestParam String id, @RequestParam(required = false) String storeId) {
|
||||
return accountService.getByIdAndStoreId(resolveStoreId(storeId), id);
|
||||
}
|
||||
|
||||
private String resolveStoreId(String requestedStoreId) {
|
||||
if (roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
return requestedStoreId;
|
||||
}
|
||||
return storeService.getBelongIdByRequiredLoginUserId();
|
||||
return accountService.getByIdAndStoreId(storeScopeService.resolveStoreId(storeId), id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPointLedgerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPointTriggerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPointAdjustRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPointLedgerPage;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
@@ -30,8 +27,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/gao/customer/point")
|
||||
public class GaoPointLedgerController {
|
||||
|
||||
private final RoleChecker roleChecker;
|
||||
private final GaoStoreService storeService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final GaoPointLedgerService ledgerService;
|
||||
private final GaoPointTriggerService triggerService;
|
||||
|
||||
@@ -41,7 +37,7 @@ public class GaoPointLedgerController {
|
||||
@PostMapping("/ledger/list")
|
||||
public PageResult<GaoPointLedger> list(@RequestBody GaoPointLedgerPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
page.setStoreId(resolveStoreId(page.getStoreId()));
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
return ledgerService.pageByQuery(page);
|
||||
}
|
||||
|
||||
@@ -50,14 +46,7 @@ public class GaoPointLedgerController {
|
||||
@RequireGaoPermission(GaoPermissionCode.POINT_ADJUST)
|
||||
@PostMapping("/adjust")
|
||||
public void adjust(@RequestBody GaoPointAdjustRequest request) {
|
||||
request.setStoreId(resolveStoreId(request.getStoreId()));
|
||||
request.setStoreId(storeScopeService.resolveStoreId(request.getStoreId()));
|
||||
triggerService.adjust(request);
|
||||
}
|
||||
|
||||
private String resolveStoreId(String requestedStoreId) {
|
||||
if (roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
return requestedStoreId;
|
||||
}
|
||||
return storeService.getBelongIdByRequiredLoginUserId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPointRuleService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
@@ -32,8 +29,7 @@ import java.util.List;
|
||||
@RequestMapping("/gao/customer/point")
|
||||
public class GaoPointRuleController {
|
||||
|
||||
private final RoleChecker roleChecker;
|
||||
private final GaoStoreService storeService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final GaoPointRuleService ruleService;
|
||||
|
||||
@AOPLog
|
||||
@@ -42,9 +38,9 @@ public class GaoPointRuleController {
|
||||
@PostMapping("/rule/list")
|
||||
public PageResult<GaoPointRule> list(@RequestBody Page<GaoPointRule> page) {
|
||||
TimiException.required(page, "not found page");
|
||||
if (!isGlobalManager()) {
|
||||
if (!storeScopeService.hasGlobalScope()) {
|
||||
GaoPointRule example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoPointRule());
|
||||
example.setStoreId(resolveStoreId(null));
|
||||
example.setStoreId(storeScopeService.resolveStoreId(example.getStoreId()));
|
||||
page.setEqualsExample(example);
|
||||
}
|
||||
return ruleService.page(page);
|
||||
@@ -57,7 +53,7 @@ public class GaoPointRuleController {
|
||||
public GaoPointRule detail(@RequestParam String id) {
|
||||
GaoPointRule rule = ruleService.get(id);
|
||||
TimiException.required(rule, "not found point rule");
|
||||
checkStore(rule.getStoreId());
|
||||
storeScopeService.checkStoreId(rule.getStoreId());
|
||||
return rule;
|
||||
}
|
||||
|
||||
@@ -66,7 +62,7 @@ public class GaoPointRuleController {
|
||||
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_CREATE)
|
||||
@PostMapping("/rule/create")
|
||||
public String create(@RequestBody GaoPointRule rule) {
|
||||
rule.setStoreId(resolveStoreId(rule.getStoreId()));
|
||||
rule.setStoreId(storeScopeService.resolveStoreId(rule.getStoreId()));
|
||||
ruleService.create(rule);
|
||||
return rule.getId();
|
||||
}
|
||||
@@ -76,7 +72,7 @@ public class GaoPointRuleController {
|
||||
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_UPDATE)
|
||||
@PostMapping("/rule/update")
|
||||
public void update(@RequestBody GaoPointRule rule) {
|
||||
rule.setStoreId(resolveStoreId(rule.getStoreId()));
|
||||
rule.setStoreId(storeScopeService.resolveStoreId(rule.getStoreId()));
|
||||
ruleService.update(rule);
|
||||
}
|
||||
|
||||
@@ -87,7 +83,7 @@ public class GaoPointRuleController {
|
||||
public void sort(@RequestBody List<String> idList) {
|
||||
if (idList != null) {
|
||||
for (GaoPointRule rule : ruleService.listByIdList(idList)) {
|
||||
checkStore(rule.getStoreId());
|
||||
storeScopeService.checkStoreId(rule.getStoreId());
|
||||
}
|
||||
}
|
||||
ruleService.sort(idList);
|
||||
@@ -100,24 +96,7 @@ public class GaoPointRuleController {
|
||||
public void delete(@RequestParam String id) {
|
||||
GaoPointRule rule = ruleService.get(id);
|
||||
TimiException.required(rule, "not found point rule");
|
||||
checkStore(rule.getStoreId());
|
||||
storeScopeService.checkStoreId(rule.getStoreId());
|
||||
ruleService.delete(id);
|
||||
}
|
||||
|
||||
private String resolveStoreId(String requestedStoreId) {
|
||||
if (isGlobalManager()) {
|
||||
return requestedStoreId;
|
||||
}
|
||||
return storeService.getBelongIdByRequiredLoginUserId();
|
||||
}
|
||||
|
||||
private void checkStore(String storeId) {
|
||||
if (!isGlobalManager()) {
|
||||
TimiException.requiredTrue(resolveStoreId(null).equals(storeId), "无权操作其他门店数据");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGlobalManager() {
|
||||
return roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name());
|
||||
}
|
||||
}
|
||||
|
||||
+23
-2
@@ -4,6 +4,9 @@ import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoStoreBusinessDay;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreBusinessDayService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
@@ -27,12 +30,17 @@ import java.util.List;
|
||||
public class GaoStoreBusinessDayController {
|
||||
|
||||
private final GaoStoreBusinessDayService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
||||
@PostMapping("/list")
|
||||
public PageResult<GaoStoreBusinessDay> list(@RequestBody Page<GaoStoreBusinessDay> page) {
|
||||
TimiException.required(page, "not found page");
|
||||
GaoStoreBusinessDay example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoStoreBusinessDay());
|
||||
example.setStoreId(storeScopeService.resolveStoreId(example.getStoreId()));
|
||||
page.setEqualsExample(example);
|
||||
return service.page(page);
|
||||
}
|
||||
|
||||
@@ -41,7 +49,7 @@ public class GaoStoreBusinessDayController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
||||
@PostMapping("/calendar")
|
||||
public List<GaoStoreBusinessDay> calendar(@RequestParam String storeId, @RequestParam Integer beginDateValue, @RequestParam Integer endDateValue) {
|
||||
return service.listByDateRange(storeId, beginDateValue, endDateValue);
|
||||
return service.listByDateRange(storeScopeService.resolveStoreId(storeId), beginDateValue, endDateValue);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@@ -49,7 +57,10 @@ public class GaoStoreBusinessDayController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
||||
@PostMapping("/detail")
|
||||
public GaoStoreBusinessDay detail(@RequestParam String id) {
|
||||
return service.get(id);
|
||||
GaoStoreBusinessDay businessDay = service.get(id);
|
||||
TimiException.required(businessDay, "not found businessDay");
|
||||
storeScopeService.checkStoreId(businessDay.getStoreId());
|
||||
return businessDay;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@@ -57,6 +68,7 @@ public class GaoStoreBusinessDayController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
||||
@PostMapping("/create")
|
||||
public String create(@RequestBody GaoStoreBusinessDay businessDay) {
|
||||
businessDay.setStoreId(storeScopeService.resolveStoreId(businessDay.getStoreId()));
|
||||
service.create(businessDay);
|
||||
return businessDay.getId();
|
||||
}
|
||||
@@ -66,6 +78,12 @@ public class GaoStoreBusinessDayController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
||||
@PostMapping("/update")
|
||||
public void update(@RequestBody GaoStoreBusinessDay businessDay) {
|
||||
TimiException.required(businessDay, "not found businessDay");
|
||||
TimiException.required(businessDay.getId(), "not found businessDay.id");
|
||||
GaoStoreBusinessDay dbBusinessDay = service.get(businessDay.getId());
|
||||
TimiException.required(dbBusinessDay, "not found businessDay");
|
||||
storeScopeService.checkStoreId(dbBusinessDay.getStoreId());
|
||||
businessDay.setStoreId(dbBusinessDay.getStoreId());
|
||||
service.update(businessDay);
|
||||
}
|
||||
|
||||
@@ -74,6 +92,9 @@ public class GaoStoreBusinessDayController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_DELETE)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestParam String id) {
|
||||
GaoStoreBusinessDay businessDay = service.get(id);
|
||||
TimiException.required(businessDay, "not found businessDay");
|
||||
storeScopeService.checkStoreId(businessDay.getStoreId());
|
||||
service.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ package com.imyeyu.api.modules.gao.controller;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreChartService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordNumberValueDailyStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -30,6 +32,7 @@ import java.util.List;
|
||||
public class GaoStoreChartController {
|
||||
|
||||
private final GaoStoreChartService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
|
||||
/// 查询客户增长趋势
|
||||
@AOPLog
|
||||
@@ -37,6 +40,8 @@ public class GaoStoreChartController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/customer/daily")
|
||||
public List<GaoCustomerDailyStateView> customerDaily(@RequestBody GaoStoreChartReq req) {
|
||||
TimiException.required(req, "not found req");
|
||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||
return service.customerDailyTrend(req);
|
||||
}
|
||||
|
||||
@@ -46,7 +51,7 @@ public class GaoStoreChartController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/customer/gender")
|
||||
public List<GaoCustomerGenderStatView> customerGender(@RequestParam String storeId) {
|
||||
return service.customerGender(storeId);
|
||||
return service.customerGender(storeScopeService.resolveStoreId(storeId));
|
||||
}
|
||||
|
||||
/// 查询登记事件每日统计,可同时支撑登记趋势和事件结构图表
|
||||
@@ -55,6 +60,8 @@ public class GaoStoreChartController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/event/record/daily")
|
||||
public List<GaoEventRecordDailyStatView> eventRecordDaily(@RequestBody GaoStoreChartReq req) {
|
||||
TimiException.required(req, "not found req");
|
||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||
return service.eventRecordDailyStat(req);
|
||||
}
|
||||
|
||||
@@ -64,6 +71,8 @@ public class GaoStoreChartController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/event/record/number-value/daily")
|
||||
public List<GaoEventRecordNumberValueDailyStatView> eventRecordNumberValueDaily(@RequestBody GaoStoreChartReq req) {
|
||||
TimiException.required(req, "not found req");
|
||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||
return service.eventRecordNumberValueDailyStat(req);
|
||||
}
|
||||
|
||||
@@ -73,6 +82,8 @@ public class GaoStoreChartController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||
@PostMapping("/event/record/calendar")
|
||||
public List<GaoEventRecordCalendarView> eventRecordCalendar(@RequestBody GaoStoreChartReq req) {
|
||||
TimiException.required(req, "not found req");
|
||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||
return service.eventRecordCalendar(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoStore;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.network.Network;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
@@ -37,14 +37,20 @@ import java.util.List;
|
||||
public class GaoStoreController {
|
||||
|
||||
private final GaoStoreService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final PermissionChecker permissionChecker;
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
||||
@PostMapping("/list")
|
||||
public PageResult<GaoStore> list(@RequestBody Page<GaoStore> page) {
|
||||
TimiException.required(page, "not found page");
|
||||
if (!storeScopeService.hasGlobalScope()) {
|
||||
GaoStore example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoStore());
|
||||
example.setId(storeScopeService.resolveStoreId(example.getId()));
|
||||
page.setEqualsExample(example);
|
||||
}
|
||||
return service.page(page);
|
||||
}
|
||||
|
||||
@@ -53,7 +59,9 @@ public class GaoStoreController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
||||
@PostMapping("/detail")
|
||||
public GaoStore detail(@RequestParam String id) {
|
||||
return service.get(id);
|
||||
GaoStore store = service.get(id);
|
||||
storeScopeService.checkStore(store);
|
||||
return store;
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@@ -70,12 +78,13 @@ public class GaoStoreController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
||||
@PostMapping("/update")
|
||||
public void update(@RequestBody GaoStore store) {
|
||||
if (permissionChecker.hasAny(ModuleCode.GAO, GaoPermissionCode.STORE_CREATE.getValue())) {
|
||||
if (storeScopeService.hasGlobalScope()) {
|
||||
// 门店管理
|
||||
service.update(store);
|
||||
} else {
|
||||
// 店长
|
||||
GaoStore dbStore = service.get(store.getId());
|
||||
storeScopeService.checkStore(dbStore);
|
||||
dbStore.setName(store.getName());
|
||||
dbStore.setTelephone(store.getTelephone());
|
||||
dbStore.setAddress(store.getAddress());
|
||||
@@ -98,6 +107,11 @@ public class GaoStoreController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
||||
@PostMapping("/sort")
|
||||
public void sort(@RequestBody List<String> idList) {
|
||||
if (!storeScopeService.hasGlobalScope()) {
|
||||
for (String id : TimiJava.safeIterable(idList)) {
|
||||
storeScopeService.checkStore(service.get(id));
|
||||
}
|
||||
}
|
||||
service.sort(idList);
|
||||
}
|
||||
|
||||
@@ -107,6 +121,12 @@ public class GaoStoreController {
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(@RequestBody Page<GaoStore> page) throws IOException {
|
||||
TimiException.required(page, "not found page");
|
||||
if (!storeScopeService.hasGlobalScope()) {
|
||||
GaoStore example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoStore());
|
||||
example.setId(storeScopeService.resolveStoreId(example.getId()));
|
||||
page.setEqualsExample(example);
|
||||
}
|
||||
HttpServletResponse resp = TimiSpring.getResponse();
|
||||
String contentDisposition = Network.getFileDownloadHeader("门店列表-%s.xlsx".formatted(Time.serialize.format(Time.now())));
|
||||
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.imyeyu.api.modules.gao.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.imyeyu.api.modules.common.entity.Article;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreDiaryService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoStoreDiaryPage;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
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.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.util.ResponseView;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/// GAO 门店日记接口
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/gao/store/diary")
|
||||
public class GaoStoreDiaryController {
|
||||
|
||||
private final GaoStoreDiaryService service;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final UserService userService;
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
/// 分页查询门店日记
|
||||
///
|
||||
/// @param page 分页查询条件
|
||||
/// @return 门店日记分页结果
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_DIARY_READ)
|
||||
@PostMapping("/list")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public PageResult<Article> list(@RequestBody GaoStoreDiaryPage page) {
|
||||
TimiException.required(page, "未找到分页参数");
|
||||
TimiException.required(page.getBeginCreatedAt(), "未找到门店日记开始时间");
|
||||
TimiException.required(page.getEndCreatedAt(), "未找到门店日记结束时间");
|
||||
TimiException.requiredTrue(page.getBeginCreatedAt() < page.getEndCreatedAt(), "门店日记时间范围无效");
|
||||
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||
PageResult<Article> result = service.page(page);
|
||||
{
|
||||
List<String> articleIdList = result.getList().stream().map(Article::getId).toList();
|
||||
Map<String, Attachment> attachMap = attachmentService.mapFirstImageByIdList(Attachment.BizType.ARTICLE, articleIdList);
|
||||
for (Article article : result.getList()) {
|
||||
Attachment attachment = attachMap.get(article.getId());
|
||||
article.setAttachmentList(TimiJava.ofList(attachment));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 查询门店日记详情
|
||||
///
|
||||
/// @param id 日记 ID
|
||||
/// @return 门店日记
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_DIARY_READ)
|
||||
@PostMapping("/detail")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public Article detail(@RequestParam String id) {
|
||||
Article article = service.get(id);
|
||||
storeScopeService.checkStoreId(article.getBizId());
|
||||
article.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.ARTICLE, article.getId()));
|
||||
if (TimiJava.isNotEmpty(article.getUpdatedBy())) {
|
||||
User updater = userService.get(article.getUpdatedBy());
|
||||
if (updater != null) {
|
||||
updater.setAttachmentList(attachmentService.listByBizId(
|
||||
Attachment.BizType.USER,
|
||||
updater.getId(),
|
||||
User.AttachType.AVATAR.toString()
|
||||
));
|
||||
}
|
||||
article.setUpdater(updater);
|
||||
}
|
||||
return article;
|
||||
}
|
||||
|
||||
/// 创建门店日记
|
||||
///
|
||||
/// @param article 门店日记
|
||||
/// @return 日记 ID
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_DIARY_CREATE)
|
||||
@PostMapping("/create")
|
||||
public String create(@RequestBody Article article) {
|
||||
article.setBizId(storeScopeService.resolveStoreId(article.getBizId()));
|
||||
service.create(article, userLoginService.getRequireLoginUserId());
|
||||
return article.getId();
|
||||
}
|
||||
|
||||
/// 修改门店日记
|
||||
///
|
||||
/// @param article 门店日记
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_DIARY_UPDATE)
|
||||
@PostMapping("/update")
|
||||
public void update(@RequestBody Article article) {
|
||||
TimiException.required(article, "未找到门店日记");
|
||||
TimiException.required(article.getId(), "未找到门店日记 ID");
|
||||
Article dbArticle = service.get(article.getId());
|
||||
storeScopeService.checkStoreId(dbArticle.getBizId());
|
||||
service.update(article, userLoginService.getRequireLoginUserId());
|
||||
}
|
||||
|
||||
/// 删除门店日记
|
||||
///
|
||||
/// @param id 日记 ID
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequireGaoPermission(GaoPermissionCode.STORE_DIARY_DELETE)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestParam String id) {
|
||||
Article article = service.get(id);
|
||||
storeScopeService.checkStoreId(article.getBizId());
|
||||
service.delete(article.getId());
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,8 @@ import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoUser;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoUserInviteService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoUserService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoUserInviteCreateRequest;
|
||||
@@ -19,13 +18,13 @@ import com.imyeyu.api.modules.gao.vo.GaoUserInviteValidateRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoUserInviteValidateResponse;
|
||||
import com.imyeyu.api.modules.user.entity.Role;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
import com.imyeyu.api.modules.user.service.RoleService;
|
||||
import com.imyeyu.api.modules.user.service.AuthorizationScopeService;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.api.modules.user.service.UserRoleService;
|
||||
import com.imyeyu.api.modules.user.service.UserService;
|
||||
import com.imyeyu.api.modules.user.vo.LoginResponse;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
@@ -53,12 +52,11 @@ import java.util.UUID;
|
||||
public class GaoUserController {
|
||||
|
||||
private final UserService userService;
|
||||
private final RoleService roleService;
|
||||
private final RoleChecker roleChecker;
|
||||
private final AuthorizationScopeService authorizationScopeService;
|
||||
private final GaoUserService service;
|
||||
private final GaoUserInviteService inviteService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final GaoStoreService gaoStoreService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
@@ -93,10 +91,11 @@ public class GaoUserController {
|
||||
@PostMapping("/list")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public PageResult<GaoUser> list(@RequestBody Page<GaoUser> page) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
TimiException.required(page, "not found page");
|
||||
if (!storeScopeService.hasGlobalScope()) {
|
||||
// 非全局管理只能看所在店用户
|
||||
page.setEqualsExample(TimiJava.defaultIfNull(page.getEqualsExample(), new GaoUser()));
|
||||
page.getEqualsExample().setStoreId(gaoStoreService.getByUserId(userLoginService.getRequireLoginUserId()).getId());
|
||||
page.getEqualsExample().setStoreId(storeScopeService.resolveStoreId(page.getEqualsExample().getStoreId()));
|
||||
}
|
||||
PageResult<GaoUser> result = service.page(page);
|
||||
{
|
||||
@@ -118,6 +117,8 @@ public class GaoUserController {
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoUser detail(@RequestParam String id) {
|
||||
GaoUser result = service.get(id);
|
||||
TimiException.required(result, "not found gao user");
|
||||
storeScopeService.checkStoreId(result.getStoreId());
|
||||
{
|
||||
User user = userService.get(result.getUserId());
|
||||
user.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.USER, user.getId()));
|
||||
@@ -135,9 +136,7 @@ public class GaoUserController {
|
||||
@PostMapping("/create")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoUser create(@RequestBody GaoUser gaoUser) {
|
||||
if (!roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||
gaoUser.setStoreId(gaoStoreService.getByUserId(userLoginService.getRequireLoginUserId()).getId());
|
||||
}
|
||||
gaoUser.setStoreId(storeScopeService.resolveStoreId(gaoUser.getStoreId()));
|
||||
service.create(gaoUser);
|
||||
return service.get(gaoUser.getId());
|
||||
}
|
||||
@@ -148,6 +147,12 @@ public class GaoUserController {
|
||||
@PostMapping("/update")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoUser update(@RequestBody GaoUser gaoUser) {
|
||||
TimiException.required(gaoUser, "not found gao user");
|
||||
TimiException.required(gaoUser.getId(), "not found gao user.id");
|
||||
GaoUser dbGaoUser = service.get(gaoUser.getId());
|
||||
TimiException.required(dbGaoUser, "not found gao user");
|
||||
storeScopeService.checkStoreId(dbGaoUser.getStoreId());
|
||||
gaoUser.setStoreId(dbGaoUser.getStoreId());
|
||||
service.update(gaoUser);
|
||||
return service.get(gaoUser.getId());
|
||||
}
|
||||
@@ -158,6 +163,12 @@ public class GaoUserController {
|
||||
@PostMapping("/transfer/store")
|
||||
@JsonView(ResponseView.Public.class)
|
||||
public GaoUser transferStore(@RequestBody GaoUser gaoUser) {
|
||||
TimiException.required(gaoUser, "not found gao user");
|
||||
TimiException.required(gaoUser.getId(), "not found gao user.id");
|
||||
GaoUser dbGaoUser = service.get(gaoUser.getId());
|
||||
TimiException.required(dbGaoUser, "not found gao user");
|
||||
storeScopeService.checkStoreId(dbGaoUser.getStoreId());
|
||||
gaoUser.setStoreId(storeScopeService.resolveStoreId(gaoUser.getStoreId()));
|
||||
service.transferStore(gaoUser);
|
||||
return service.get(gaoUser.getId());
|
||||
}
|
||||
@@ -167,6 +178,9 @@ public class GaoUserController {
|
||||
@RequireGaoPermission(GaoPermissionCode.USER_DELETE)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestParam String id) {
|
||||
GaoUser gaoUser = service.get(id);
|
||||
TimiException.required(gaoUser, "not found gao user");
|
||||
storeScopeService.checkStoreId(gaoUser.getStoreId());
|
||||
service.delete(id);
|
||||
}
|
||||
|
||||
@@ -175,7 +189,6 @@ public class GaoUserController {
|
||||
@RequireGaoPermission(GaoPermissionCode.USER_READ)
|
||||
@PostMapping("/role/list")
|
||||
public List<Role> roleList() {
|
||||
// TODO 此接口获取本模块所有角色,需要限制角色
|
||||
return roleService.listByModuleCode(ModuleCode.GAO);
|
||||
return authorizationScopeService.listGrantableRole(userLoginService.getRequireLoginUserId(), ModuleCode.GAO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 客户实际购买的套餐
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoCustomerPackage extends UUIDEntity {
|
||||
|
||||
/// 客户套餐状态
|
||||
public enum Status {
|
||||
|
||||
/// 履约中
|
||||
ACTIVE,
|
||||
|
||||
/// 已全部履约
|
||||
FULFILLED,
|
||||
|
||||
/// 已取消
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
/// 付款状态
|
||||
public enum PaymentStatus {
|
||||
|
||||
/// 未付款
|
||||
UNPAID,
|
||||
|
||||
/// 部分付款
|
||||
PARTIALLY_PAID,
|
||||
|
||||
/// 已付清
|
||||
PAID
|
||||
}
|
||||
|
||||
/// 退款状态
|
||||
public enum RefundStatus {
|
||||
|
||||
/// 无需退款
|
||||
NOT_REQUIRED,
|
||||
|
||||
/// 待退款或待确认无需退款
|
||||
PENDING,
|
||||
|
||||
/// 已完成退款处理
|
||||
REFUNDED
|
||||
}
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 来源套餐配置 ID
|
||||
private String packageConfigId;
|
||||
|
||||
/// 购买事件登记 ID
|
||||
private String purchaseEventRecordId;
|
||||
|
||||
/// 购买幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 套餐名称快照
|
||||
private String packageNameSnapshot;
|
||||
|
||||
/// 标准价快照,单位分
|
||||
private Long standardAmountSnapshot;
|
||||
|
||||
/// 实际成交价,单位分
|
||||
private Long saleAmount;
|
||||
|
||||
/// 待核销收款合计,单位分
|
||||
private Long pendingPaidAmount;
|
||||
|
||||
/// 累计有效收款,单位分
|
||||
private Long paidAmount;
|
||||
|
||||
/// 累计有效退款,单位分
|
||||
private Long refundedAmount;
|
||||
|
||||
/// 套餐状态
|
||||
private Status status;
|
||||
|
||||
/// 付款状态
|
||||
private PaymentStatus paymentStatus;
|
||||
|
||||
/// 退款状态
|
||||
private RefundStatus refundStatus;
|
||||
|
||||
/// 购买时间
|
||||
private Long purchasedAt;
|
||||
|
||||
/// 购买登记人
|
||||
private String operatorUserId;
|
||||
|
||||
/// 取消时间
|
||||
private Long cancelledAt;
|
||||
|
||||
/// 取消操作人
|
||||
private String cancelOperatorUserId;
|
||||
|
||||
/// 取消原因
|
||||
private String cancelReason;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 客户
|
||||
@Transient
|
||||
private GaoCustomer customer;
|
||||
|
||||
/// 购买事件登记
|
||||
@Transient
|
||||
private GaoEventRecord purchaseEventRecord;
|
||||
|
||||
/// 购买操作人
|
||||
@Transient
|
||||
private User operator;
|
||||
|
||||
/// 取消操作人
|
||||
@Transient
|
||||
private User cancelOperator;
|
||||
|
||||
/// 产品权益列表
|
||||
@Transient
|
||||
private List<GaoCustomerPackageItem> itemList;
|
||||
|
||||
/// 发放历史
|
||||
@Transient
|
||||
private List<GaoPackageDelivery> deliveryList;
|
||||
|
||||
/// 资金历史
|
||||
@Transient
|
||||
private List<GaoFundRecord> fundRecordList;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/// GAO 客户套餐产品权益快照
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoCustomerPackageItem extends UUIDEntity {
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 产品名称快照
|
||||
private String productNameSnapshot;
|
||||
|
||||
/// 产品单位快照
|
||||
private String unitSnapshot;
|
||||
|
||||
/// 总权益数量
|
||||
private BigDecimal totalQuantity;
|
||||
|
||||
/// 待核销占用数量
|
||||
private BigDecimal reservedQuantity;
|
||||
|
||||
/// 已核销净发放数量
|
||||
private BigDecimal deliveredQuantity;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 待核销数量
|
||||
@Transient
|
||||
private BigDecimal pendingQuantity;
|
||||
|
||||
/// 当前可发数量
|
||||
@Transient
|
||||
private BigDecimal availableQuantity;
|
||||
|
||||
/// 权益剩余数量
|
||||
@Transient
|
||||
private BigDecimal remainingQuantity;
|
||||
}
|
||||
@@ -18,6 +18,16 @@ import java.util.List;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoEvent extends UUIDEntity {
|
||||
|
||||
/// 事件业务类型
|
||||
public enum BizType {
|
||||
|
||||
/// 普通登记事件
|
||||
GENERAL,
|
||||
|
||||
/// 购买产品套餐
|
||||
PACKAGE_PURCHASE
|
||||
}
|
||||
|
||||
///
|
||||
/// 登记事件状态
|
||||
///
|
||||
@@ -93,6 +103,16 @@ public class GaoEvent extends UUIDEntity {
|
||||
/// 统计图表颜色
|
||||
private String color;
|
||||
|
||||
/// 业务类型
|
||||
private BizType bizType;
|
||||
|
||||
/// 购买套餐事件绑定的套餐配置 ID
|
||||
private String packageConfigId;
|
||||
|
||||
/// 购买套餐事件绑定的套餐配置
|
||||
@Transient
|
||||
private GaoPackageConfig packageConfig;
|
||||
|
||||
/// 登记事件名称
|
||||
private String name;
|
||||
|
||||
@@ -117,6 +137,9 @@ public class GaoEvent extends UUIDEntity {
|
||||
/// true 为登记时要求拍照
|
||||
private Boolean requirePhoto;
|
||||
|
||||
/// true 为登记记录需要核销
|
||||
private Boolean requireVerification;
|
||||
|
||||
/// 登记限制类型
|
||||
private LimitType limitType;
|
||||
|
||||
@@ -135,4 +158,8 @@ public class GaoEvent extends UUIDEntity {
|
||||
/// 可登记角色代码列表
|
||||
@Transient
|
||||
private List<String> roleCodeList;
|
||||
|
||||
/// 登记事件选项组列表
|
||||
@Transient
|
||||
private List<GaoEventOptionGroup> optionGroupList;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/// 登记事件选项
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoEventOption extends UUIDEntity {
|
||||
|
||||
/// 选项组 ID
|
||||
private String groupId;
|
||||
|
||||
/// 选项名称
|
||||
private String label;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// 登记事件选项组
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoEventOptionGroup extends UUIDEntity {
|
||||
|
||||
/// 选项选择模式
|
||||
public enum SelectionMode {
|
||||
|
||||
/// 只能选择一个选项
|
||||
SINGLE,
|
||||
|
||||
/// 可以选择多个选项
|
||||
MULTIPLE
|
||||
}
|
||||
|
||||
/// 登记事件 ID
|
||||
private String eventId;
|
||||
|
||||
/// 选项组名称
|
||||
private String name;
|
||||
|
||||
/// 选择模式
|
||||
private SelectionMode selectionMode;
|
||||
|
||||
/// true 为登记时必须选择至少一个选项
|
||||
private Boolean required;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
|
||||
/// 选项列表
|
||||
@Transient
|
||||
private List<GaoEventOption> optionList;
|
||||
}
|
||||
@@ -18,6 +18,23 @@ import java.util.List;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoEventRecord extends UUIDEntity {
|
||||
|
||||
///
|
||||
/// 登记记录核销状态
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-29
|
||||
public enum VerificationStatus {
|
||||
|
||||
/// 该事件不需要核销
|
||||
NOT_REQUIRED,
|
||||
|
||||
/// 等待核销
|
||||
PENDING,
|
||||
|
||||
/// 已核销
|
||||
VERIFIED
|
||||
}
|
||||
|
||||
///
|
||||
/// 登记事件记录附件类型
|
||||
///
|
||||
@@ -49,6 +66,15 @@ public class GaoEventRecord extends UUIDEntity {
|
||||
/// 本次登记提交的整数业务数值
|
||||
private Long numberValue;
|
||||
|
||||
/// 核销状态
|
||||
private VerificationStatus verificationStatus;
|
||||
|
||||
/// 核销用户 ID
|
||||
private String verifierUserId;
|
||||
|
||||
/// 核销时间
|
||||
private Long verifiedAt;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
@@ -61,6 +87,9 @@ public class GaoEventRecord extends UUIDEntity {
|
||||
/// 营业日递增序号
|
||||
private Long businessDayNo;
|
||||
|
||||
/// 最后修改用户 ID
|
||||
private String updatedBy;
|
||||
|
||||
@Transient
|
||||
private GaoCustomer customer;
|
||||
|
||||
@@ -70,10 +99,21 @@ public class GaoEventRecord extends UUIDEntity {
|
||||
@Transient
|
||||
private User operator;
|
||||
|
||||
@Transient
|
||||
private User verifier;
|
||||
|
||||
/// 最后修改用户
|
||||
@Transient
|
||||
private User updater;
|
||||
|
||||
@Transient
|
||||
private List<Attachment> attachmentList;
|
||||
|
||||
/// 客户 ID 列表,批量登记时使用
|
||||
@Transient
|
||||
private List<String> customerIdList;
|
||||
|
||||
/// 登记记录已选择的选项组列表
|
||||
@Transient
|
||||
private List<GaoEventRecordOptionGroup> selectedOptionGroupList;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/// 登记事件记录选项关系
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoEventRecordOption extends UUIDEntity {
|
||||
|
||||
/// 登记事件记录 ID
|
||||
private String eventRecordId;
|
||||
|
||||
/// 选项组 ID
|
||||
private String groupId;
|
||||
|
||||
/// 选项 ID
|
||||
private String optionId;
|
||||
|
||||
/// 选项组排序快照
|
||||
private Integer groupSort;
|
||||
|
||||
/// 选项排序快照
|
||||
private Integer optionSort;
|
||||
|
||||
/// 选项组名称快照
|
||||
private String groupNameSnapshot;
|
||||
|
||||
/// 选项名称快照
|
||||
private String optionLabelSnapshot;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// 登记事件记录选项组视图
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
@Data
|
||||
public class GaoEventRecordOptionGroup {
|
||||
|
||||
/// 选项组 ID
|
||||
private String groupId;
|
||||
|
||||
/// 选项组名称
|
||||
private String groupName;
|
||||
|
||||
/// 已选择的选项列表
|
||||
private List<GaoEventOption> selectedOptionList;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 通用资金操作流水
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoFundRecord extends UUIDEntity {
|
||||
|
||||
/// 资金业务类型
|
||||
public enum BizType {
|
||||
|
||||
/// 客户套餐
|
||||
CUSTOMER_PACKAGE
|
||||
}
|
||||
|
||||
/// 资金操作类型
|
||||
public enum OperationType {
|
||||
|
||||
/// 线下收款
|
||||
RECEIVE,
|
||||
|
||||
/// 退款
|
||||
REFUND,
|
||||
|
||||
/// 确认无需退款
|
||||
CONFIRM_NO_REFUND,
|
||||
|
||||
/// 资金操作冲正
|
||||
REVOKE
|
||||
}
|
||||
|
||||
/// 资金操作状态
|
||||
public enum Status {
|
||||
|
||||
/// 待店长核销
|
||||
PENDING,
|
||||
|
||||
/// 已生效
|
||||
VERIFIED,
|
||||
|
||||
/// 店长已拒绝
|
||||
REJECTED,
|
||||
|
||||
/// 提交人已撤销
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 资金业务类型
|
||||
private BizType bizType;
|
||||
|
||||
/// 业务对象 ID
|
||||
private String bizId;
|
||||
|
||||
/// 操作类型
|
||||
private OperationType operationType;
|
||||
|
||||
/// 操作状态
|
||||
private Status status;
|
||||
|
||||
/// 金额,单位分
|
||||
private Long amount;
|
||||
|
||||
/// 冲正时关联原资金记录
|
||||
private String sourceRecordId;
|
||||
|
||||
/// 幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 提交操作的用户 ID
|
||||
private String operatorUserId;
|
||||
|
||||
/// 收款核销用户 ID
|
||||
private String verifierUserId;
|
||||
|
||||
/// 收款核销时间
|
||||
private Long verifiedAt;
|
||||
|
||||
/// 线下操作发生时间
|
||||
private Long occurredAt;
|
||||
|
||||
/// 原因
|
||||
private String reason;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 操作人
|
||||
@Transient
|
||||
private User operator;
|
||||
|
||||
/// 核销人
|
||||
@Transient
|
||||
private User verifier;
|
||||
|
||||
/// 附件
|
||||
@Transient
|
||||
private List<Attachment> attachmentList;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 套餐配置
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoPackageConfig extends UUIDEntity {
|
||||
|
||||
/// 套餐配置状态
|
||||
public enum Status {
|
||||
|
||||
/// 草稿,不可销售
|
||||
DRAFT,
|
||||
|
||||
/// 启用,可在有效期内销售
|
||||
ACTIVE,
|
||||
|
||||
/// 停用,不可销售
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
/// 套餐名称
|
||||
private String name;
|
||||
|
||||
/// 套餐说明
|
||||
private String description;
|
||||
|
||||
/// 标准总价,单位分
|
||||
private Long standardAmount;
|
||||
|
||||
/// 状态
|
||||
private Status status;
|
||||
|
||||
/// 生效开始时间戳,毫秒,空为不限制
|
||||
private Long beginAt;
|
||||
|
||||
/// 生效结束时间戳,毫秒,空为不限制
|
||||
private Long endAt;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
|
||||
/// 最后修改用户 ID
|
||||
private String updatedBy;
|
||||
|
||||
/// 最后修改用户
|
||||
@Transient
|
||||
private User updater;
|
||||
|
||||
/// 配置产品列表
|
||||
@Transient
|
||||
private List<GaoPackageConfigItem> itemList;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/// GAO 套餐配置产品明细
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoPackageConfigItem extends UUIDEntity {
|
||||
|
||||
/// 套餐配置 ID
|
||||
private String packageConfigId;
|
||||
|
||||
/// 产品名称
|
||||
private String productName;
|
||||
|
||||
/// 产品单位
|
||||
private String unit;
|
||||
|
||||
/// 默认数量
|
||||
private BigDecimal defaultQuantity;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.user.entity.User;
|
||||
import com.imyeyu.spring.annotation.table.Transient;
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 套餐发放操作
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoPackageDelivery extends UUIDEntity {
|
||||
|
||||
/// 发放操作类型
|
||||
public enum OperationType {
|
||||
|
||||
/// 正常发放
|
||||
DELIVER,
|
||||
|
||||
/// 整单冲正
|
||||
REVOKE
|
||||
}
|
||||
|
||||
/// 发放状态
|
||||
public enum Status {
|
||||
|
||||
/// 待核销
|
||||
PENDING,
|
||||
|
||||
/// 已核销
|
||||
VERIFIED,
|
||||
|
||||
/// 店长已拒绝
|
||||
REJECTED,
|
||||
|
||||
/// 提交人已撤销
|
||||
CANCELLED,
|
||||
|
||||
/// 已核销后被整单冲正
|
||||
REVOKED
|
||||
}
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 操作类型
|
||||
private OperationType operationType;
|
||||
|
||||
/// 发放状态
|
||||
private Status status;
|
||||
|
||||
/// 客户端幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 提交人
|
||||
private String operatorUserId;
|
||||
|
||||
/// 实际发放时间
|
||||
private Long deliveredAt;
|
||||
|
||||
/// 核销或拒绝操作人
|
||||
private String verifierUserId;
|
||||
|
||||
/// 核销或拒绝时间
|
||||
private Long verifiedAt;
|
||||
|
||||
/// 冲正单指向原发放单
|
||||
private String sourceDeliveryId;
|
||||
|
||||
/// 拒绝、撤销或冲正原因
|
||||
private String reason;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 操作人
|
||||
@Transient
|
||||
private User operator;
|
||||
|
||||
/// 核销或拒绝操作人
|
||||
@Transient
|
||||
private User verifier;
|
||||
|
||||
/// 原发放单
|
||||
@Transient
|
||||
private GaoPackageDelivery sourceDelivery;
|
||||
|
||||
/// 发放明细
|
||||
@Transient
|
||||
private List<GaoPackageDeliveryItem> itemList;
|
||||
|
||||
/// 附件
|
||||
@Transient
|
||||
private List<Attachment> attachmentList;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.imyeyu.api.modules.gao.entity;
|
||||
|
||||
import com.imyeyu.spring.entity.UUIDEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/// GAO 套餐发放产品明细
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoPackageDeliveryItem extends UUIDEntity {
|
||||
|
||||
/// 发放单 ID
|
||||
private String deliveryId;
|
||||
|
||||
/// 客户套餐产品 ID
|
||||
private String customerPackageItemId;
|
||||
|
||||
/// 本次数量
|
||||
private BigDecimal quantity;
|
||||
|
||||
/// 操作前占用数量快照
|
||||
private BigDecimal reservedBefore;
|
||||
|
||||
/// 操作后占用数量快照
|
||||
private BigDecimal reservedAfter;
|
||||
|
||||
/// 操作前已发数量快照
|
||||
private BigDecimal deliveredBefore;
|
||||
|
||||
/// 操作后已发数量快照
|
||||
private BigDecimal deliveredAfter;
|
||||
|
||||
/// 产品名称快照
|
||||
private String productNameSnapshot;
|
||||
|
||||
/// 产品单位快照
|
||||
private String unitSnapshot;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -48,4 +49,11 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
|
||||
/// @param page 查询条件
|
||||
/// @return 客户列表
|
||||
List<GaoCustomer> selectByQuery(GaoCustomerPage page);
|
||||
|
||||
/// 原子调整邀请客户数
|
||||
///
|
||||
/// @param customerId 客户 ID
|
||||
/// @param delta 变化量
|
||||
@Update("UPDATE `gao_customer` SET `invited_count` = GREATEST(IFNULL(`invited_count`, 0) + #{delta}, 0), `updated_at` = " + UNIX_TIME + " WHERE `id` = #{customerId}" + NOT_DELETE)
|
||||
void updateInvitedCountDelta(@Param("customerId") String customerId, @Param("delta") Long delta);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 客户套餐产品权益 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoCustomerPackageItemMapper extends BaseMapper<GaoCustomerPackageItem, String> {
|
||||
|
||||
/// 按客户套餐查询产品权益
|
||||
List<GaoCustomerPackageItem> selectByCustomerPackageId(@Param("customerPackageId") String customerPackageId);
|
||||
|
||||
/// 批量查询客户套餐产品权益
|
||||
List<GaoCustomerPackageItem> selectByCustomerPackageIdList(@Param("customerPackageIdList") Collection<String> customerPackageIdList);
|
||||
|
||||
/// 锁定客户套餐产品
|
||||
GaoCustomerPackageItem selectForUpdate(@Param("id") String id);
|
||||
|
||||
/// 调整待核销占用数量
|
||||
int updateReservedQuantity(@Param("id") String id, @Param("delta") java.math.BigDecimal delta);
|
||||
|
||||
/// 调整已核销发放数量
|
||||
int updateDeliveredQuantity(@Param("id") String id, @Param("delta") java.math.BigDecimal delta);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/// GAO 客户套餐 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoCustomerPackageMapper extends BaseMapper<GaoCustomerPackage, String> {
|
||||
|
||||
/// 按门店和请求号查询购买结果
|
||||
GaoCustomerPackage selectByRequestNo(@Param("storeId") String storeId, @Param("requestNo") String requestNo);
|
||||
|
||||
/// 按请求号查询购买结果,用于配置删除后的幂等重试
|
||||
GaoCustomerPackage selectByRequestNoAnyStore(@Param("requestNo") String requestNo);
|
||||
|
||||
/// 锁定客户套餐
|
||||
GaoCustomerPackage selectForUpdate(@Param("id") String id);
|
||||
|
||||
/// 按购买事件登记查询客户套餐
|
||||
GaoCustomerPackage selectByPurchaseEventRecordId(@Param("purchaseEventRecordId") String purchaseEventRecordId);
|
||||
|
||||
/// 按条件统计客户套餐
|
||||
long countByQuery(GaoCustomerPackagePage page);
|
||||
|
||||
/// 按条件查询客户套餐
|
||||
java.util.List<GaoCustomerPackage> selectByQuery(GaoCustomerPackagePage page);
|
||||
|
||||
/// 原子调整待核销收款
|
||||
int updatePendingPaidAmountDelta(@Param("id") String id, @Param("delta") Long delta);
|
||||
|
||||
/// 原子调整有效收款
|
||||
int updatePaidAmountDelta(@Param("id") String id, @Param("delta") Long delta);
|
||||
|
||||
/// 带状态条件更新套餐状态
|
||||
int updateStatusIf(@Param("id") String id, @Param("fromStatus") GaoCustomerPackage.Status fromStatus, @Param("toStatus") GaoCustomerPackage.Status toStatus, @Param("cancelledAt") Long cancelledAt, @Param("cancelOperatorUserId") String cancelOperatorUserId, @Param("cancelReason") String cancelReason, @Param("refundStatus") GaoCustomerPackage.RefundStatus refundStatus);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.SelectProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -14,7 +17,16 @@ import java.util.List;
|
||||
/// @since 2026-07-27 15:53
|
||||
public interface GaoEventMapper extends BaseMapper<GaoEvent, String> {
|
||||
|
||||
@SelectProvider(type = GaoEventSQLProvider.class, method = "selectPageWithoutDeleted")
|
||||
List<GaoEvent> selectPageWithoutDeleted(Page<GaoEvent> page);
|
||||
|
||||
@SelectProvider(type = GaoEventSQLProvider.class, method = "countPageWithoutDeleted")
|
||||
long countPageWithoutDeleted(Page<GaoEvent> page);
|
||||
|
||||
List<GaoEvent> selectByIdList(Collection<String> idList);
|
||||
|
||||
List<GaoEvent> selectValidList(@Param("storeId") String storeId, @Param("now") Long now);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM `gao_event` WHERE `package_config_id` = #{packageConfigId} AND `deleted_at` IS NULL")
|
||||
long countByPackageConfigId(String packageConfigId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOptionGroup;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// 登记事件选项组 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
public interface GaoEventOptionGroupMapper extends BaseMapper<GaoEventOptionGroup, String> {
|
||||
|
||||
List<GaoEventOptionGroup> selectAllByEventIdList(@Param("eventIdList") Collection<String> eventIdList);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOption;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// 登记事件选项 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
public interface GaoEventOptionMapper extends BaseMapper<GaoEventOption, String> {
|
||||
|
||||
List<GaoEventOption> selectAllByGroupIdList(@Param("groupIdList") Collection<String> groupIdList);
|
||||
}
|
||||
@@ -8,9 +8,11 @@ import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordNumberValueDailyStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventOptionStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -20,6 +22,13 @@ import java.util.List;
|
||||
/// @since 2026-07-27 14:11
|
||||
public interface GaoEventRecordMapper extends BaseMapper<GaoEventRecord, String> {
|
||||
|
||||
/// 查询事件登记记录数量
|
||||
///
|
||||
/// @param eventId 事件 ID
|
||||
/// @return 登记记录数量
|
||||
@Select("SELECT COUNT(*) FROM `gao_event_record` WHERE `event_id` = #{eventId} AND `deleted_at` IS NULL")
|
||||
long countByEventId(String eventId);
|
||||
|
||||
Long count(String storeId, String customerId, String eventId, Long beginAt, Long endAt);
|
||||
|
||||
/// 查询事件中已携带数值的登记记录数量
|
||||
@@ -44,6 +53,15 @@ public interface GaoEventRecordMapper extends BaseMapper<GaoEventRecord, String>
|
||||
/// @param earnedPointValue 获得积分总和
|
||||
void updateEarnedPointValue(@Param("id") String id, @Param("earnedPointValue") Long earnedPointValue);
|
||||
|
||||
/// 原子更新登记记录核销状态
|
||||
///
|
||||
/// @param id 登记记录 ID
|
||||
/// @param verifierUserId 核销用户 ID
|
||||
/// @param verifiedAt 核销时间
|
||||
/// @param updatedBy 最后修改用户 ID
|
||||
/// @return 更新行数
|
||||
int verify(@Param("id") String id, @Param("verifierUserId") String verifierUserId, @Param("verifiedAt") Long verifiedAt, @Param("updatedBy") String updatedBy);
|
||||
|
||||
/// 按门店和登记日期统计登记记录数
|
||||
///
|
||||
/// @param storeId 门店 ID
|
||||
@@ -71,6 +89,12 @@ public interface GaoEventRecordMapper extends BaseMapper<GaoEventRecord, String>
|
||||
/// @return 登记数值合计
|
||||
Long sumNumberValueByRange(GaoEventRecordPage page);
|
||||
|
||||
/// 按选项统计登记记录数量
|
||||
///
|
||||
/// @param page 登记记录范围查询请求
|
||||
/// @return 选项统计列表
|
||||
List<GaoEventOptionStatView> selectOptionStatByRange(GaoEventRecordPage page);
|
||||
|
||||
/// 统计客户登记排行榜总数
|
||||
///
|
||||
/// @param page 排行榜分页请求
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecordOption;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// 登记事件记录选项 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
public interface GaoEventRecordOptionMapper extends BaseMapper<GaoEventRecordOption, String> {
|
||||
|
||||
List<GaoEventRecordOption> selectAllByEventRecordIdList(@Param("eventRecordIdList") Collection<String> eventRecordIdList);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.util.SQLProvider;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
|
||||
/// 登记事件分页查询 SQL 提供器
|
||||
///
|
||||
/// @author Codex
|
||||
public class GaoEventSQLProvider extends SQLProvider {
|
||||
|
||||
/// 分页查询未删除的登记事件
|
||||
public String selectPageWithoutDeleted(ProviderContext context, @Param("page") Page<?> page) {
|
||||
EntityMeta meta = getEntityMeta(context);
|
||||
return appendWithoutDeleted(buildSelectByPageSQL(meta, meta.getTable(), page, "#{offset}", "#{limit}"));
|
||||
}
|
||||
|
||||
/// 统计未删除的登记事件
|
||||
public String countPageWithoutDeleted(ProviderContext context, @Param("page") Page<?> page) {
|
||||
EntityMeta meta = getEntityMeta(context);
|
||||
return appendWithoutDeleted(buildCountByPageSQL(meta, meta.getTable(), page));
|
||||
}
|
||||
|
||||
private String appendWithoutDeleted(String sql) {
|
||||
return sql.replace(
|
||||
"WHERE 1 = 1",
|
||||
"WHERE 1 = 1 AND (`status` IS NULL OR `status` <> '%s')".formatted(GaoEvent.Status.DELETED.name())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRecordPage;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 资金流水 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoFundRecordMapper extends BaseMapper<GaoFundRecord, String> {
|
||||
|
||||
/// 按门店和请求号查询资金操作
|
||||
GaoFundRecord selectByRequestNo(@Param("storeId") String storeId, @Param("requestNo") String requestNo);
|
||||
|
||||
/// 锁定资金记录
|
||||
GaoFundRecord selectForUpdate(@Param("id") String id);
|
||||
|
||||
/// 查询资金冲正记录
|
||||
GaoFundRecord selectRevokeBySourceRecordId(@Param("sourceRecordId") String sourceRecordId);
|
||||
|
||||
/// 查询客户套餐待核销收款数量
|
||||
long countPendingReceiveByBizId(@Param("bizId") String bizId);
|
||||
|
||||
/// 查询客户套餐资金历史
|
||||
List<GaoFundRecord> selectByBizId(@Param("bizId") String bizId);
|
||||
|
||||
/// 按条件统计资金记录
|
||||
long countByQuery(GaoFundRecordPage page);
|
||||
|
||||
/// 按条件查询资金记录
|
||||
List<GaoFundRecord> selectByQuery(GaoFundRecordPage page);
|
||||
|
||||
/// 带状态条件更新资金记录
|
||||
int updateStatusIf(@Param("id") String id, @Param("fromStatus") GaoFundRecord.Status fromStatus, @Param("toStatus") GaoFundRecord.Status toStatus, @Param("verifierUserId") String verifierUserId, @Param("verifiedAt") Long verifiedAt, @Param("reason") String reason);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 套餐配置产品 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoPackageConfigItemMapper extends BaseMapper<GaoPackageConfigItem, String> {
|
||||
|
||||
/// 按套餐配置 ID 查询产品
|
||||
List<GaoPackageConfigItem> selectByPackageConfigId(@Param("packageConfigId") String packageConfigId);
|
||||
|
||||
/// 批量查询套餐配置产品
|
||||
List<GaoPackageConfigItem> selectByPackageConfigIdList(@Param("packageConfigIdList") Collection<String> packageConfigIdList);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 套餐配置 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoPackageConfigMapper extends BaseMapper<GaoPackageConfig, String> {
|
||||
|
||||
/// 按 ID 列表查询套餐配置
|
||||
List<GaoPackageConfig> selectByIdList(@Param("idList") Collection<String> idList);
|
||||
|
||||
/// 查询有效可购买套餐
|
||||
List<GaoPackageConfig> selectValidList(@Param("storeId") String storeId, @Param("now") Long now);
|
||||
|
||||
/// 按条件统计套餐配置
|
||||
long countByQuery(GaoPackageConfigPage page);
|
||||
|
||||
/// 按条件查询套餐配置
|
||||
List<GaoPackageConfig> selectByQuery(GaoPackageConfigPage page);
|
||||
|
||||
/// 查询门店内同名配置
|
||||
GaoPackageConfig selectByStoreIdAndName(@Param("storeId") String storeId, @Param("name") String name, @Param("excludeId") String excludeId);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDeliveryItem;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 套餐发放明细 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoPackageDeliveryItemMapper extends BaseMapper<GaoPackageDeliveryItem, String> {
|
||||
|
||||
/// 按发放单查询明细
|
||||
List<GaoPackageDeliveryItem> selectByDeliveryId(@Param("deliveryId") String deliveryId);
|
||||
|
||||
/// 批量查询发放明细
|
||||
List<GaoPackageDeliveryItem> selectByDeliveryIdList(@Param("deliveryIdList") Collection<String> deliveryIdList);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDelivery;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryPage;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 套餐发放 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoPackageDeliveryMapper extends BaseMapper<GaoPackageDelivery, String> {
|
||||
|
||||
/// 按门店和请求号查询发放结果
|
||||
GaoPackageDelivery selectByRequestNo(@Param("storeId") String storeId, @Param("requestNo") String requestNo);
|
||||
|
||||
/// 锁定发放单
|
||||
GaoPackageDelivery selectForUpdate(@Param("id") String id);
|
||||
|
||||
/// 查询原发放单的冲正记录
|
||||
GaoPackageDelivery selectRevokeBySourceDeliveryId(@Param("sourceDeliveryId") String sourceDeliveryId);
|
||||
|
||||
/// 查询客户套餐待核销发放数量
|
||||
long countPendingByCustomerPackageId(@Param("customerPackageId") String customerPackageId);
|
||||
|
||||
/// 查询客户套餐已核销正常发放数量
|
||||
long countVerifiedByCustomerPackageId(@Param("customerPackageId") String customerPackageId);
|
||||
|
||||
/// 查询客户套餐发放历史
|
||||
java.util.List<GaoPackageDelivery> selectByCustomerPackageId(@Param("customerPackageId") String customerPackageId);
|
||||
|
||||
/// 按条件统计发放单
|
||||
long countByQuery(GaoPackageDeliveryPage page);
|
||||
|
||||
/// 按条件查询发放单
|
||||
List<GaoPackageDelivery> selectByQuery(GaoPackageDeliveryPage page);
|
||||
|
||||
/// 带状态条件更新发放单
|
||||
int updateStatusIf(@Param("id") String id, @Param("fromStatus") GaoPackageDelivery.Status fromStatus, @Param("toStatus") GaoPackageDelivery.Status toStatus, @Param("verifierUserId") String verifierUserId, @Param("verifiedAt") Long verifiedAt, @Param("reason") String reason);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Article;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoStoreDiaryPage;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 门店日记 Mapper
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoStoreDiaryMapper extends BaseMapper<Article, String> {
|
||||
|
||||
/// 查询月份内的门店日记
|
||||
///
|
||||
/// @param page 月份分页查询请求
|
||||
/// @return 门店日记列表
|
||||
List<Article> selectByMonth(GaoStoreDiaryPage page);
|
||||
|
||||
/// 统计月份内的门店日记数量
|
||||
///
|
||||
/// @param page 月份分页查询请求
|
||||
/// @return 门店日记数量
|
||||
long countByMonth(GaoStoreDiaryPage page);
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.imyeyu.api.modules.gao.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoUser;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
///
|
||||
/// GAO 用户 Mapper
|
||||
@@ -9,4 +14,10 @@ import com.imyeyu.spring.mapper.BaseMapper;
|
||||
/// @author Codex
|
||||
/// @since 2026-07-30
|
||||
public interface GaoUserMapper extends BaseMapper<GaoUser, String> {
|
||||
|
||||
List<GaoUser> selectByUserIdList(Collection<String> userIdList);
|
||||
|
||||
List<GaoUser> selectRecipientPage(@Param("page") Page<GaoUser> page);
|
||||
|
||||
long countRecipients(@Param("page") Page<GaoUser> page);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackageCancelRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseRequest;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/// GAO 客户套餐服务
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoCustomerPackageService extends BaseService<GaoCustomerPackage, String> {
|
||||
|
||||
/// 按条件查询客户套餐
|
||||
PageResult<GaoCustomerPackage> pageByQuery(GaoCustomerPackagePage page);
|
||||
|
||||
/// 创建客户套餐及购买事件登记
|
||||
GaoCustomerPackage purchase(GaoCustomerPackagePurchaseRequest request);
|
||||
|
||||
/// 取消客户套餐
|
||||
GaoCustomerPackage cancel(GaoCustomerPackageCancelRequest request);
|
||||
|
||||
/// 锁定客户套餐
|
||||
///
|
||||
/// @param id 客户套餐 ID
|
||||
/// @return 已锁定的客户套餐,不存在时为空
|
||||
GaoCustomerPackage getForUpdate(String id);
|
||||
|
||||
/// 查询客户套餐产品权益
|
||||
///
|
||||
/// @param customerPackageId 客户套餐 ID
|
||||
/// @return 产品权益列表
|
||||
List<GaoCustomerPackageItem> listItemByCustomerPackageId(String customerPackageId);
|
||||
|
||||
/// 批量查询客户套餐产品权益
|
||||
///
|
||||
/// @param customerPackageIdList 客户套餐 ID 列表
|
||||
/// @return 按客户套餐 ID 分组的产品权益
|
||||
Map<String, List<GaoCustomerPackageItem>> mapItemListByCustomerPackageIdList(Collection<String> customerPackageIdList);
|
||||
|
||||
/// 锁定客户套餐产品权益
|
||||
///
|
||||
/// @param itemId 客户套餐产品权益 ID
|
||||
/// @return 已锁定的产品权益,不存在时为空
|
||||
GaoCustomerPackageItem getItemForUpdate(String itemId);
|
||||
|
||||
/// 原子调整待核销发放占用数量
|
||||
///
|
||||
/// @param itemId 客户套餐产品权益 ID
|
||||
/// @param delta 调整量
|
||||
/// @return 受影响行数
|
||||
int updateItemReservedQuantity(String itemId, BigDecimal delta);
|
||||
|
||||
/// 原子调整已核销发放数量
|
||||
///
|
||||
/// @param itemId 客户套餐产品权益 ID
|
||||
/// @param delta 调整量
|
||||
/// @return 受影响行数
|
||||
int updateItemDeliveredQuantity(String itemId, BigDecimal delta);
|
||||
|
||||
/// 原子调整待核销收款金额
|
||||
///
|
||||
/// @param id 客户套餐 ID
|
||||
/// @param delta 调整量
|
||||
/// @return 受影响行数
|
||||
int updatePendingPaidAmountDelta(String id, Long delta);
|
||||
|
||||
/// 原子调整有效收款金额
|
||||
///
|
||||
/// @param id 客户套餐 ID
|
||||
/// @param delta 调整量
|
||||
/// @return 受影响行数
|
||||
int updatePaidAmountDelta(String id, Long delta);
|
||||
|
||||
/// 保存客户套餐聚合状态
|
||||
///
|
||||
/// @param packageEntity 客户套餐聚合
|
||||
void updateAggregateState(GaoCustomerPackage packageEntity);
|
||||
}
|
||||
@@ -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.GaoEventRecord;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventOptionStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage;
|
||||
@@ -24,12 +25,34 @@ public interface GaoEventRecordService extends BaseService<GaoEventRecord, Strin
|
||||
/// @return 登记事件记录列表
|
||||
List<GaoEventRecord> listByCustomerId(String customerId);
|
||||
|
||||
/// 为登记记录附加已选择的选项组
|
||||
///
|
||||
/// @param recordList 登记记录列表
|
||||
void attachSelectedOptionGroupList(List<GaoEventRecord> recordList);
|
||||
|
||||
/// 核销登记事件记录
|
||||
///
|
||||
/// @param id 登记记录 ID
|
||||
void verify(String id);
|
||||
|
||||
default void createBatch(List<GaoEventRecord> recordList) {
|
||||
for (GaoEventRecord record : TimiJava.safeIterable(recordList)) {
|
||||
create(record);
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建单条普通事件登记
|
||||
///
|
||||
/// @param record 事件登记
|
||||
/// @return 创建后的事件登记
|
||||
GaoEventRecord createOne(GaoEventRecord record);
|
||||
|
||||
/// 由套餐购买服务创建购买事件登记
|
||||
///
|
||||
/// @param record 购买事件登记
|
||||
/// @return 创建后的事件登记
|
||||
GaoEventRecord createPackagePurchaseRecord(GaoEventRecord record);
|
||||
|
||||
void createWithCustomer(GaoCustomer customer);
|
||||
|
||||
/// 按任意时间范围查询登记事件记录
|
||||
@@ -44,6 +67,12 @@ public interface GaoEventRecordService extends BaseService<GaoEventRecord, Strin
|
||||
/// @return 登记数值合计
|
||||
Long sumNumberValueByRange(GaoEventRecordPage req);
|
||||
|
||||
/// 按选项统计登记记录数量
|
||||
///
|
||||
/// @param req 登记记录范围查询请求
|
||||
/// @return 选项统计列表
|
||||
List<GaoEventOptionStatView> optionStatByRange(GaoEventRecordPage req);
|
||||
|
||||
/// 按时间范围查询客户登记排行榜
|
||||
///
|
||||
/// @param page 范围查询请求,不传时间为全部时间
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOptionGroup;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -14,6 +17,12 @@ import java.util.Map;
|
||||
/// @since 2026-07-27
|
||||
public interface GaoEventService extends BaseService<GaoEvent, String> {
|
||||
|
||||
/// 分页查询未删除的登记事件
|
||||
///
|
||||
/// @param page 分页参数
|
||||
/// @return 登记事件分页结果
|
||||
PageResult<GaoEvent> pageWithoutDeleted(Page<GaoEvent> page);
|
||||
|
||||
Map<String, GaoEvent> mapByIdList(Collection<String> idList);
|
||||
|
||||
Map<String, List<String>> mapRoleCodeListByEventIdList(Collection<String> eventIdList);
|
||||
@@ -24,6 +33,17 @@ public interface GaoEventService extends BaseService<GaoEvent, String> {
|
||||
/// @return 登记事件列表
|
||||
List<GaoEvent> listValid(String storeId);
|
||||
|
||||
/// 为登记事件附加选项组和选项
|
||||
///
|
||||
/// @param eventList 登记事件列表
|
||||
void attachOptionGroupList(Collection<GaoEvent> eventList);
|
||||
|
||||
/// 更新登记事件的选项组配置
|
||||
///
|
||||
/// @param eventId 登记事件 ID
|
||||
/// @param optionGroupList 选项组配置列表
|
||||
void updateOptionGroupList(String eventId, List<GaoEventOptionGroup> optionGroupList);
|
||||
|
||||
/// 删除门店下的全部登记事件及角色关联
|
||||
///
|
||||
/// @param storeId 门店 ID
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundActionRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundConfirmNoRefundRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRecordPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundReceiveRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRefundRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRevokeRequest;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 通用资金流水服务
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoFundRecordService extends BaseService<GaoFundRecord, String> {
|
||||
|
||||
/// 按条件查询资金流水
|
||||
PageResult<GaoFundRecord> pageByQuery(GaoFundRecordPage page);
|
||||
|
||||
/// 查询客户套餐资金历史
|
||||
List<GaoFundRecord> listByCustomerPackageId(String customerPackageId);
|
||||
|
||||
/// 登记线下收款
|
||||
GaoFundRecord receive(GaoFundReceiveRequest request);
|
||||
|
||||
/// 核销收款
|
||||
GaoFundRecord verify(String fundRecordId);
|
||||
|
||||
/// 拒绝待核销收款
|
||||
GaoFundRecord reject(GaoFundActionRequest request);
|
||||
|
||||
/// 撤销自己提交的待核销收款
|
||||
GaoFundRecord cancel(GaoFundActionRequest request);
|
||||
|
||||
/// 登记退款
|
||||
GaoFundRecord refund(GaoFundRefundRequest request);
|
||||
|
||||
/// 确认无需退款
|
||||
GaoFundRecord confirmNoRefund(GaoFundConfirmNoRefundRequest request);
|
||||
|
||||
/// 冲正资金流水
|
||||
GaoFundRecord revoke(GaoFundRevokeRequest request);
|
||||
|
||||
/// 统计客户套餐待核销收款数量
|
||||
///
|
||||
/// @param bizId 业务 ID
|
||||
/// @return 待核销收款数量
|
||||
long countPendingReceiveByBizId(String bizId);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/// GAO 套餐配置服务
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoPackageConfigService extends BaseService<GaoPackageConfig, String> {
|
||||
|
||||
/// 按条件查询套餐配置
|
||||
PageResult<GaoPackageConfig> pageByQuery(GaoPackageConfigPage page);
|
||||
|
||||
/// 查询有效可购买套餐
|
||||
List<GaoPackageConfig> listValid(String storeId);
|
||||
|
||||
/// 批量查询套餐配置
|
||||
///
|
||||
/// @param idList 套餐配置 ID 列表
|
||||
/// @return 按套餐配置 ID 映射的配置
|
||||
Map<String, GaoPackageConfig> mapByIdList(Collection<String> idList);
|
||||
|
||||
/// 查询套餐配置产品明细
|
||||
///
|
||||
/// @param packageConfigId 套餐配置 ID
|
||||
/// @return 产品明细列表
|
||||
List<GaoPackageConfigItem> listItemByPackageConfigId(String packageConfigId);
|
||||
|
||||
/// 批量查询套餐配置产品明细
|
||||
///
|
||||
/// @param packageConfigIdList 套餐配置 ID 列表
|
||||
/// @return 按套餐配置 ID 分组的产品明细
|
||||
Map<String, List<GaoPackageConfigItem>> mapItemListByPackageConfigIdList(Collection<String> packageConfigIdList);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDelivery;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDeliveryItem;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryActionRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryCreateRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryRevokeRequest;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/// GAO 套餐发放服务
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoPackageDeliveryService extends BaseService<GaoPackageDelivery, String> {
|
||||
|
||||
/// 按条件查询发放单
|
||||
PageResult<GaoPackageDelivery> pageByQuery(GaoPackageDeliveryPage page);
|
||||
|
||||
/// 查询客户套餐发放历史
|
||||
List<GaoPackageDelivery> listByCustomerPackageId(String customerPackageId);
|
||||
|
||||
/// 批量查询发放明细
|
||||
///
|
||||
/// @param deliveryIdList 发放单 ID 列表
|
||||
/// @return 按发放单 ID 分组的发放明细
|
||||
Map<String, List<GaoPackageDeliveryItem>> mapItemListByDeliveryIdList(Collection<String> deliveryIdList);
|
||||
|
||||
/// 创建待核销发放单
|
||||
GaoPackageDelivery createDelivery(GaoPackageDeliveryCreateRequest request);
|
||||
|
||||
/// 核销发放单
|
||||
GaoPackageDelivery verify(String deliveryId);
|
||||
|
||||
/// 拒绝待核销发放单
|
||||
GaoPackageDelivery reject(GaoPackageDeliveryActionRequest request);
|
||||
|
||||
/// 撤销自己提交的待核销发放单
|
||||
GaoPackageDelivery cancel(GaoPackageDeliveryActionRequest request);
|
||||
|
||||
/// 整单冲正已核销发放单
|
||||
GaoPackageDelivery revoke(GaoPackageDeliveryRevokeRequest request);
|
||||
|
||||
/// 统计客户套餐待核销发放单数量
|
||||
///
|
||||
/// @param customerPackageId 客户套餐 ID
|
||||
/// @return 待核销发放单数量
|
||||
long countPendingByCustomerPackageId(String customerPackageId);
|
||||
|
||||
/// 统计客户套餐已核销正常发放单数量
|
||||
///
|
||||
/// @param customerPackageId 客户套餐 ID
|
||||
/// @return 已核销正常发放单数量
|
||||
long countVerifiedByCustomerPackageId(String customerPackageId);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Article;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoStoreDiaryPage;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
|
||||
/// GAO 门店日记服务
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
public interface GaoStoreDiaryService {
|
||||
|
||||
/// 分页查询门店日记
|
||||
///
|
||||
/// @param page 分页查询条件
|
||||
/// @return 门店日记分页结果
|
||||
PageResult<Article> page(GaoStoreDiaryPage page);
|
||||
|
||||
/// 查询门店日记详情
|
||||
///
|
||||
/// @param id 日记 ID
|
||||
/// @return 门店日记
|
||||
Article get(String id);
|
||||
|
||||
/// 创建门店日记
|
||||
///
|
||||
/// @param article 门店日记
|
||||
/// @param userId 操作用户 ID
|
||||
void create(Article article, String userId);
|
||||
|
||||
/// 修改门店日记
|
||||
///
|
||||
/// @param article 门店日记
|
||||
/// @param userId 操作用户 ID
|
||||
void update(Article article, String userId);
|
||||
|
||||
/// 删除门店日记
|
||||
///
|
||||
/// @param id 日记 ID
|
||||
void delete(String id);
|
||||
|
||||
/// 删除门店下的全部日记
|
||||
///
|
||||
/// @param storeId 门店 ID
|
||||
void deleteByStoreId(String storeId);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoStore;
|
||||
|
||||
/// GAO 门店资源范围服务
|
||||
///
|
||||
/// 同时拥有门店管理和店长角色时,按门店管理角色使用全局范围
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-23
|
||||
public interface GaoStoreScopeService {
|
||||
|
||||
/// 判断当前用户是否拥有 GAO 全局范围
|
||||
///
|
||||
/// @return `true` 为允许访问全部门店
|
||||
boolean hasGlobalScope();
|
||||
|
||||
/// 将请求门店 ID 解析为当前可用门店 ID
|
||||
///
|
||||
/// @param requestedStoreId 请求中的门店 ID,可为空
|
||||
/// @return 门店 ID,全局范围可为空
|
||||
String resolveStoreId(String requestedStoreId);
|
||||
|
||||
/// 检查门店是否在当前用户可管理范围内
|
||||
///
|
||||
/// @param storeId 门店 ID
|
||||
void checkStoreId(String storeId);
|
||||
|
||||
/// 检查门店实体是否在当前用户可管理范围内
|
||||
///
|
||||
/// @param store 门店
|
||||
void checkStore(GaoStore store);
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
package com.imyeyu.api.modules.gao.service;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoUser;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.BaseService;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
///
|
||||
/// GAO 用户服务
|
||||
///
|
||||
@@ -19,4 +24,16 @@ public interface GaoUserService extends BaseService<GaoUser, String> {
|
||||
///
|
||||
/// @param gaoUser GAO 用户,必须包含 ID 和目标门店 ID
|
||||
void transferStore(GaoUser gaoUser);
|
||||
|
||||
/// 根据核心用户 ID 映射 GAO 用户
|
||||
///
|
||||
/// @param userIdList 核心用户 ID 列表
|
||||
/// @return GAO 用户映射
|
||||
Map<String, GaoUser> mapByUserIdList(Collection<String> userIdList);
|
||||
|
||||
/// 查询可作为通知接收人的启用用户
|
||||
///
|
||||
/// @param page 分页参数
|
||||
/// @return 接收用户分页结果
|
||||
PageResult<GaoUser> pageRecipients(Page<GaoUser> page);
|
||||
}
|
||||
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
package com.imyeyu.api.modules.gao.service.implement;
|
||||
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageItemMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageDeliveryService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackageCancelRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseItem;
|
||||
import com.imyeyu.api.modules.system.entity.Logger;
|
||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
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.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// GAO 客户套餐服务实现
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GaoCustomerPackageServiceImplement extends AbstractEntityService<GaoCustomerPackage, String> implements GaoCustomerPackageService {
|
||||
|
||||
private static final BigDecimal ZERO = BigDecimal.ZERO;
|
||||
|
||||
private final GaoCustomerPackageMapper mapper;
|
||||
private final GaoCustomerPackageItemMapper itemMapper;
|
||||
private final ObjectProvider<GaoPackageDeliveryService> deliveryServiceProvider;
|
||||
private final ObjectProvider<GaoFundRecordService> fundRecordServiceProvider;
|
||||
private final GaoPackageConfigService packageConfigService;
|
||||
private final GaoCustomerService customerService;
|
||||
private final GaoEventService eventService;
|
||||
private final GaoEventRecordService eventRecordService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final PermissionChecker permissionChecker;
|
||||
private final LoggerService loggerService;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<GaoCustomerPackage, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoCustomerPackage getForUpdate(String id) {
|
||||
return mapper.selectForUpdate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoCustomerPackageItem> listItemByCustomerPackageId(String customerPackageId) {
|
||||
return itemMapper.selectByCustomerPackageId(customerPackageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoCustomerPackageItem getItemForUpdate(String itemId) {
|
||||
return itemMapper.selectForUpdate(itemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateItemReservedQuantity(String itemId, BigDecimal delta) {
|
||||
return itemMapper.updateReservedQuantity(itemId, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateItemDeliveredQuantity(String itemId, BigDecimal delta) {
|
||||
return itemMapper.updateDeliveredQuantity(itemId, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updatePendingPaidAmountDelta(String id, Long delta) {
|
||||
return mapper.updatePendingPaidAmountDelta(id, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updatePaidAmountDelta(String id, Long delta) {
|
||||
return mapper.updatePaidAmountDelta(id, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAggregateState(GaoCustomerPackage packageEntity) {
|
||||
mapper.update(packageEntity);
|
||||
}
|
||||
|
||||
/// 客户套餐必须通过购买流程创建
|
||||
///
|
||||
/// @param packageEntity 客户套餐
|
||||
@Override
|
||||
public void create(GaoCustomerPackage packageEntity) {
|
||||
TimiException.requiredTrue(false, "客户套餐必须通过购买接口创建");
|
||||
}
|
||||
|
||||
/// 客户套餐创建后不允许通用修改
|
||||
///
|
||||
/// @param packageEntity 客户套餐
|
||||
@Override
|
||||
public void update(GaoCustomerPackage packageEntity) {
|
||||
TimiException.requiredTrue(false, "客户套餐不允许通用修改");
|
||||
}
|
||||
|
||||
/// 客户套餐不允许通用删除
|
||||
///
|
||||
/// @param id 客户套餐 ID
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
TimiException.requiredTrue(false, "客户套餐不允许删除");
|
||||
}
|
||||
|
||||
/// 客户套餐不允许物理删除
|
||||
///
|
||||
/// @param id 客户套餐 ID
|
||||
@Override
|
||||
public void destroy(String id) {
|
||||
TimiException.requiredTrue(false, "客户套餐不允许物理删除");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoCustomerPackage> pageByQuery(GaoCustomerPackagePage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
PageResult<GaoCustomerPackage> result = new PageResult<>();
|
||||
result.setTotal(mapper.countByQuery(page));
|
||||
result.setList(mapper.selectByQuery(page));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoCustomerPackage get(String id) {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoCustomerPackage purchase(GaoCustomerPackagePurchaseRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_PURCHASE", request, () -> purchaseInternal(request));
|
||||
}
|
||||
|
||||
private GaoCustomerPackage purchaseInternal(GaoCustomerPackagePurchaseRequest request) {
|
||||
TimiException.required(request, "not found purchase request");
|
||||
TimiException.required(request.getRequestNo(), "not found requestNo");
|
||||
request.setRequestNo(request.getRequestNo().trim());
|
||||
TimiException.required(request.getEventId(), "not found eventId");
|
||||
TimiException.required(request.getCustomerId(), "not found customerId");
|
||||
|
||||
GaoEvent event = validatePurchaseEvent(request.getEventId());
|
||||
storeScopeService.checkStoreId(event.getStoreId());
|
||||
GaoPackageConfig config = packageConfigService.get(event.getPackageConfigId());
|
||||
TimiException.required(config, "购买事件绑定的套餐配置不存在");
|
||||
TimiException.requiredTrue(event.getStoreId().equals(config.getStoreId()), "购买事件和套餐配置不属于同一门店");
|
||||
List<GaoPackageConfigItem> configItemList = packageConfigService.listItemByPackageConfigId(config.getId());
|
||||
request.setSaleAmount(TimiJava.defaultIfNull(request.getSaleAmount(), config.getStandardAmount()));
|
||||
TimiException.requiredTrue(0 <= request.getSaleAmount(), "成交价不能为负数");
|
||||
if (request.getItemList() == null) {
|
||||
request.setItemList(defaultPurchaseItems(configItemList));
|
||||
}
|
||||
List<GaoCustomerPackagePurchaseItem> itemList = new java.util.ArrayList<>(request.getItemList());
|
||||
verifyPurchaseItems(itemList);
|
||||
itemList.sort(java.util.Comparator.comparing(GaoCustomerPackagePurchaseItem::getSort));
|
||||
request.setItemList(itemList);
|
||||
|
||||
GaoCustomerPackage existing = mapper.selectByRequestNo(config.getStoreId(), request.getRequestNo());
|
||||
if (existing != null) {
|
||||
verifyPurchaseIdempotency(existing, request);
|
||||
return existing;
|
||||
}
|
||||
|
||||
Long purchasedAt = TimiJava.defaultIfNull(request.getPurchasedAt(), Time.now());
|
||||
Long now = Time.now();
|
||||
TimiException.requiredTrue(config.getStatus() == GaoPackageConfig.Status.ACTIVE, "套餐配置不可购买");
|
||||
TimiException.requiredTrue(config.getBeginAt() == null || config.getBeginAt() <= now, "套餐配置尚未生效");
|
||||
TimiException.requiredTrue(config.getEndAt() == null || now <= config.getEndAt(), "套餐配置已过期");
|
||||
GaoCustomer customer = validateCustomer(request.getCustomerId(), config.getStoreId());
|
||||
verifySameStructureAsConfig(itemList, configItemList);
|
||||
boolean canOverridePriceAndQuantity = permissionChecker.hasAny(ModuleCode.GAO, com.imyeyu.api.modules.gao.bean.GaoPermissionCode.CUSTOMER_PACKAGE_PRICE_OVERRIDE.getValue());
|
||||
if (!canOverridePriceAndQuantity) {
|
||||
TimiException.requiredTrue(request.getSaleAmount().equals(config.getStandardAmount()), "普通员工不能修改套餐成交价");
|
||||
verifySameQuantityAsConfig(itemList, configItemList);
|
||||
}
|
||||
|
||||
GaoEventRecord eventRecord = new GaoEventRecord();
|
||||
eventRecord.setCustomerId(customer.getId());
|
||||
eventRecord.setStoreId(config.getStoreId());
|
||||
eventRecord.setEventId(event.getId());
|
||||
eventRecord.setRegisteredAt(purchasedAt);
|
||||
eventRecord.setRemark(request.getRemark());
|
||||
if (event.getValueType() == GaoEvent.ValueType.NUMBER && event.getValueUnit() == GaoEvent.ValueUnit.CENT) {
|
||||
eventRecord.setNumberValue(request.getSaleAmount());
|
||||
}
|
||||
GaoEventRecord createdEventRecord = eventRecordService.createPackagePurchaseRecord(eventRecordWithAttachments(eventRecord, request));
|
||||
|
||||
GaoCustomerPackage result = new GaoCustomerPackage();
|
||||
result.setStoreId(config.getStoreId());
|
||||
result.setCustomerId(customer.getId());
|
||||
result.setPackageConfigId(config.getId());
|
||||
result.setPurchaseEventRecordId(createdEventRecord.getId());
|
||||
result.setRequestNo(request.getRequestNo());
|
||||
result.setPackageNameSnapshot(config.getName());
|
||||
result.setStandardAmountSnapshot(config.getStandardAmount());
|
||||
result.setSaleAmount(request.getSaleAmount());
|
||||
result.setPendingPaidAmount(0L);
|
||||
result.setPaidAmount(0L);
|
||||
result.setRefundedAmount(0L);
|
||||
result.setStatus(GaoCustomerPackage.Status.ACTIVE);
|
||||
result.setPaymentStatus(request.getSaleAmount() == 0 ? GaoCustomerPackage.PaymentStatus.PAID : GaoCustomerPackage.PaymentStatus.UNPAID);
|
||||
result.setRefundStatus(GaoCustomerPackage.RefundStatus.NOT_REQUIRED);
|
||||
result.setPurchasedAt(purchasedAt);
|
||||
result.setOperatorUserId(userLoginService.getRequireLoginUserId());
|
||||
result.setRemark(request.getRemark());
|
||||
super.create(result);
|
||||
for (GaoCustomerPackagePurchaseItem source : itemList) {
|
||||
GaoCustomerPackageItem item = new GaoCustomerPackageItem();
|
||||
item.setCustomerPackageId(result.getId());
|
||||
item.setProductNameSnapshot(source.getProductName().trim());
|
||||
item.setUnitSnapshot(source.getUnit().trim());
|
||||
item.setTotalQuantity(source.getTotalQuantity());
|
||||
item.setReservedQuantity(ZERO);
|
||||
item.setDeliveredQuantity(ZERO);
|
||||
item.setSort(source.getSort());
|
||||
item.setRemark(source.getRemark());
|
||||
itemMapper.insert(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoCustomerPackage cancel(GaoCustomerPackageCancelRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_CANCEL", request, () -> cancelInternal(request));
|
||||
}
|
||||
|
||||
private GaoCustomerPackage cancelInternal(GaoCustomerPackageCancelRequest request) {
|
||||
TimiException.required(request, "not found cancel request");
|
||||
TimiException.required(request.getCustomerPackageId(), "not found customerPackageId");
|
||||
String reason = requiredReason(request.getReason(), "取消原因不能为空");
|
||||
GaoCustomerPackage packageEntity = mapper.selectForUpdate(request.getCustomerPackageId());
|
||||
TimiException.required(packageEntity, "not found customer package");
|
||||
storeScopeService.checkStoreId(packageEntity.getStoreId());
|
||||
TimiException.requiredTrue(packageEntity.getStatus() == GaoCustomerPackage.Status.ACTIVE, "当前套餐不可取消");
|
||||
TimiException.requiredTrue(0 == deliveryServiceProvider.getObject().countPendingByCustomerPackageId(packageEntity.getId()), "存在待核销发放,不能取消套餐");
|
||||
TimiException.requiredTrue(0 == fundRecordServiceProvider.getObject().countPendingReceiveByBizId(packageEntity.getId()), "存在待核销收款,不能取消套餐");
|
||||
TimiException.requiredTrue(0 == pendingAmount(packageEntity), "存在待核销收款,不能取消套餐");
|
||||
List<GaoCustomerPackageItem> itemList = itemMapper.selectByCustomerPackageId(packageEntity.getId());
|
||||
for (GaoCustomerPackageItem item : itemList) {
|
||||
TimiException.requiredTrue(item.getDeliveredQuantity().compareTo(item.getTotalQuantity()) < 0, "已全部履约的套餐不能取消");
|
||||
}
|
||||
packageEntity.setStatus(GaoCustomerPackage.Status.CANCELLED);
|
||||
packageEntity.setCancelledAt(Time.now());
|
||||
packageEntity.setCancelOperatorUserId(userLoginService.getRequireLoginUserId());
|
||||
packageEntity.setCancelReason(reason);
|
||||
packageEntity.setRefundStatus(0 < safeLong(packageEntity.getPaidAmount()) ? GaoCustomerPackage.RefundStatus.PENDING : GaoCustomerPackage.RefundStatus.NOT_REQUIRED);
|
||||
mapper.update(packageEntity);
|
||||
return packageEntity;
|
||||
}
|
||||
|
||||
private GaoEventRecord eventRecordWithAttachments(GaoEventRecord record, GaoCustomerPackagePurchaseRequest request) {
|
||||
record.setAttachmentList(request.getAttachmentList());
|
||||
return record;
|
||||
}
|
||||
|
||||
private GaoCustomer validateCustomer(String customerId, String storeId) {
|
||||
GaoCustomer customer = customerService.get(customerId);
|
||||
TimiException.required(customer, "not found customer");
|
||||
TimiException.requiredTrue(storeId.equals(customer.getStoreId()), "客户和套餐不属于同一门店");
|
||||
return customer;
|
||||
}
|
||||
|
||||
private GaoEvent validatePurchaseEvent(String eventId) {
|
||||
GaoEvent event = eventService.get(eventId);
|
||||
TimiException.required(event, "not found purchase event");
|
||||
TimiException.requiredTrue(event.getBizType() == GaoEvent.BizType.PACKAGE_PURCHASE, "事件不是套餐购买事件");
|
||||
TimiException.required(event.getPackageConfigId(), "购买事件未绑定套餐配置");
|
||||
return event;
|
||||
}
|
||||
|
||||
private List<GaoCustomerPackagePurchaseItem> defaultPurchaseItems(List<GaoPackageConfigItem> configItemList) {
|
||||
return configItemList.stream().map(configItem -> {
|
||||
GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem();
|
||||
item.setProductName(configItem.getProductName());
|
||||
item.setUnit(configItem.getUnit());
|
||||
item.setTotalQuantity(configItem.getDefaultQuantity());
|
||||
item.setSort(configItem.getSort());
|
||||
item.setRemark(configItem.getRemark());
|
||||
return item;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private void verifyPurchaseItems(List<GaoCustomerPackagePurchaseItem> itemList) {
|
||||
TimiException.requiredTrue(!itemList.isEmpty(), "购买套餐至少需要一个产品");
|
||||
Set<String> keySet = new HashSet<>();
|
||||
for (int i = 0; i < itemList.size(); i++) {
|
||||
GaoCustomerPackagePurchaseItem item = itemList.get(i);
|
||||
TimiException.required(item, "购买产品明细不能为空");
|
||||
TimiException.required(item.getProductName(), "购买产品名称不能为空");
|
||||
TimiException.required(item.getUnit(), "购买产品单位不能为空");
|
||||
TimiException.required(item.getTotalQuantity(), "购买产品数量不能为空");
|
||||
TimiException.requiredTrue(0 < item.getTotalQuantity().signum(), "购买产品数量必须大于 0");
|
||||
TimiException.requiredTrue(3 >= item.getTotalQuantity().scale(), "购买产品数量最多保留三位小数");
|
||||
String key = item.getProductName().trim() + "|" + item.getUnit().trim();
|
||||
TimiException.requiredTrue(keySet.add(key), "购买套餐内产品名称和单位不能重复");
|
||||
item.setProductName(item.getProductName().trim());
|
||||
item.setUnit(item.getUnit().trim());
|
||||
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifySameStructureAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
||||
TimiException.requiredTrue(itemList.size() == configItemList.size(), "套餐权益项目必须与事件绑定套餐一致");
|
||||
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getProductName() + "|" + item.getUnit(), Function.identity()));
|
||||
for (GaoPackageConfigItem configItem : configItemList) {
|
||||
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getProductName() + "|" + configItem.getUnit());
|
||||
TimiException.required(item, "套餐权益项目必须与事件绑定套餐一致");
|
||||
TimiException.requiredTrue(item.getSort().equals(configItem.getSort())
|
||||
&& sameText(item.getRemark(), configItem.getRemark()), "套餐权益项目的名称、单位、排序和备注不可修改");
|
||||
}
|
||||
}
|
||||
|
||||
private void verifySameQuantityAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
||||
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getProductName() + "|" + item.getUnit(), Function.identity()));
|
||||
for (GaoPackageConfigItem configItem : configItemList) {
|
||||
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getProductName() + "|" + configItem.getUnit());
|
||||
TimiException.requiredTrue(item.getTotalQuantity().compareTo(configItem.getDefaultQuantity()) == 0, "普通员工不能修改套餐权益配额");
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyPurchaseIdempotency(GaoCustomerPackage existing, GaoCustomerPackagePurchaseRequest request) {
|
||||
TimiException.requiredTrue(existing.getCustomerId().equals(request.getCustomerId())
|
||||
&& existing.getSaleAmount().equals(request.getSaleAmount()), "requestNo 已被其他购买请求使用");
|
||||
GaoEventRecord eventRecord = eventRecordService.get(existing.getPurchaseEventRecordId());
|
||||
TimiException.required(eventRecord, "购买事件登记不存在");
|
||||
TimiException.requiredTrue(eventRecord.getEventId().equals(request.getEventId())
|
||||
&& (request.getPurchasedAt() == null || eventRecord.getRegisteredAt().equals(request.getPurchasedAt()))
|
||||
&& sameText(existing.getRemark(), request.getRemark()), "requestNo 对应购买请求内容不一致");
|
||||
List<GaoCustomerPackageItem> existingItemList = itemMapper.selectByCustomerPackageId(existing.getId());
|
||||
List<GaoCustomerPackagePurchaseItem> requestItemList = TimiJava.defaultIfNull(request.getItemList(), List.of());
|
||||
verifyPurchaseItems(requestItemList);
|
||||
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应购买请求内容不一致");
|
||||
Map<String, GaoCustomerPackageItem> existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getProductNameSnapshot() + "|" + item.getUnitSnapshot(), Function.identity()));
|
||||
for (GaoCustomerPackagePurchaseItem item : requestItemList) {
|
||||
GaoCustomerPackageItem existingItem = existingItemMap.get(item.getProductName() + "|" + item.getUnit());
|
||||
TimiException.required(existingItem, "requestNo 对应购买请求内容不一致");
|
||||
TimiException.requiredTrue(existingItem.getTotalQuantity().compareTo(item.getTotalQuantity()) == 0
|
||||
&& existingItem.getSort().equals(item.getSort())
|
||||
&& sameText(existingItem.getRemark(), item.getRemark()), "requestNo 对应购买请求内容不一致");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<GaoCustomerPackageItem>> mapItemListByCustomerPackageIdList(Collection<String> customerPackageIdList) {
|
||||
if (customerPackageIdList == null || customerPackageIdList.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return itemMapper.selectByCustomerPackageIdList(customerPackageIdList).stream()
|
||||
.collect(Collectors.groupingBy(GaoCustomerPackageItem::getCustomerPackageId));
|
||||
}
|
||||
|
||||
private Long pendingAmount(GaoCustomerPackage packageEntity) {
|
||||
return safeLong(packageEntity.getPendingPaidAmount());
|
||||
}
|
||||
|
||||
private long safeLong(Long value) {
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
|
||||
private String requiredReason(String reason, String message) {
|
||||
TimiException.required(reason, message);
|
||||
String result = reason.trim();
|
||||
TimiException.requiredTrue(!result.isEmpty(), message);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean sameText(String left, String right) {
|
||||
return left == null ? right == null : left.equals(right);
|
||||
}
|
||||
}
|
||||
+10
-11
@@ -117,12 +117,10 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
customer.setAge(ChronoUnit.YEARS.between(customer.getBirthdate(), LocalDate.now()));
|
||||
}
|
||||
customer.setInvitedCount(0L);
|
||||
if (customer.hasIntroducer()) {
|
||||
customer.setInvitedCount(1L);
|
||||
}
|
||||
customer.setCreatorUserId(userLoginService.getRequireLoginUserId());
|
||||
|
||||
super.create(customer);
|
||||
changeInvitedCount(customer.getIntroducerCustomerId(), 1);
|
||||
// 标签
|
||||
if (customer.getTagApply() != null) {
|
||||
tagService.apply(TagApply.BizType.GAO_CUSTOMER, customer.getId(), customer.getTagApply().getTagIdList());
|
||||
@@ -159,17 +157,18 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
logger.setContent(jackson.writeValueAsString(customer));
|
||||
GaoCustomer dbCustomer = get(customer.getId());
|
||||
TimiException.requiredTrue(dbCustomer.getStoreId().equals(customer.getStoreId()), "customer.storeId invalid");
|
||||
dbCustomer.setCode(TimiJava.defaultIfEmpty(customer.getCode(), null));
|
||||
String code = TimiJava.defaultIfEmpty(customer.getCode(), null);
|
||||
{
|
||||
if (!Objects.equals(dbCustomer.getCode(), customer.getCode()) && TimiJava.isNotEmpty(customer.getCode())) {
|
||||
GaoCustomer byCode = getByCode(customer.getStoreId(), customer.getCode());
|
||||
TimiException.requiredTrue(byCode == null || !byCode.getId().equals(dbCustomer.getId()), "该姓名客户已登记");
|
||||
if (!Objects.equals(dbCustomer.getCode(), code) && TimiJava.isNotEmpty(code)) {
|
||||
GaoCustomer byCode = getByCode(customer.getStoreId(), code);
|
||||
TimiException.requiredTrue(byCode == null || byCode.getId().equals(dbCustomer.getId()), "该编码客户已登记");
|
||||
}
|
||||
if (!Objects.equals(dbCustomer.getName(), customer.getName())) {
|
||||
GaoCustomer byName = getByName(customer.getStoreId(), customer.getName());
|
||||
TimiException.requiredTrue(byName == null || !byName.getId().equals(dbCustomer.getId()), "该姓名客户已登记");
|
||||
}
|
||||
}
|
||||
dbCustomer.setCode(code);
|
||||
dbCustomer.setName(customer.getName());
|
||||
fillPinyin(dbCustomer);
|
||||
dbCustomer.setGender(customer.getGender());
|
||||
@@ -369,10 +368,12 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
try {
|
||||
logger.setContent(customerId);
|
||||
GaoCustomer customer = get(customerId);
|
||||
TimiException.required(customer, "not found customer");
|
||||
String oldIntroducerCustomerId = customer.getIntroducerCustomerId();
|
||||
customer.setIntroducerCustomerId(null);
|
||||
mapper.update(customer);
|
||||
|
||||
changeInvitedCount(customer.getIntroducerCustomerId(), -1);
|
||||
changeInvitedCount(oldIntroducerCustomerId, -1);
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
logger.setResult(customerId);
|
||||
} catch (TimiException e) {
|
||||
@@ -434,9 +435,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
if (TimiJava.isEmpty(customerId) || delta == 0) {
|
||||
return;
|
||||
}
|
||||
GaoCustomer customer = get(customerId);
|
||||
customer.setInvitedCount(customer.getInvitedCount() + delta);
|
||||
mapper.update(customer);
|
||||
mapper.updateInvitedCountDelta(customerId, delta);
|
||||
}
|
||||
|
||||
private void fillPinyin(GaoCustomer customer) {
|
||||
|
||||
+224
@@ -7,8 +7,13 @@ import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOption;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOptionGroup;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecordOption;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecordOptionGroup;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordOptionMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||
@@ -16,6 +21,7 @@ import com.imyeyu.api.modules.gao.service.GaoPointTriggerService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreBusinessDayService;
|
||||
import com.imyeyu.api.modules.gao.util.EventRecordXSSFBuilder;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventOptionStatView;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage;
|
||||
import com.imyeyu.api.modules.system.entity.Logger;
|
||||
@@ -43,7 +49,11 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/// 登记事件记录服务实现
|
||||
///
|
||||
@@ -67,6 +77,7 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
private final GaoStoreBusinessDayService businessDayService;
|
||||
|
||||
private final GaoEventRecordMapper mapper;
|
||||
private final GaoEventRecordOptionMapper recordOptionMapper;
|
||||
|
||||
private final EventRecordXSSFBuilder xssfBuilder;
|
||||
|
||||
@@ -83,9 +94,89 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
return mapper.selectAllByExample(example);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachSelectedOptionGroupList(List<GaoEventRecord> recordList) {
|
||||
List<String> recordIdList = new ArrayList<>();
|
||||
for (GaoEventRecord record : TimiJava.safeIterable(recordList)) {
|
||||
if (record != null && TimiJava.isNotEmpty(record.getId())) {
|
||||
recordIdList.add(record.getId());
|
||||
}
|
||||
}
|
||||
if (recordIdList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Map<String, GaoEventRecordOptionGroup>> groupMap = new LinkedHashMap<>();
|
||||
for (GaoEventRecordOption relation : recordOptionMapper.selectAllByEventRecordIdList(recordIdList)) {
|
||||
Map<String, GaoEventRecordOptionGroup> recordGroupMap = groupMap.computeIfAbsent(relation.getEventRecordId(), key -> new LinkedHashMap<>());
|
||||
GaoEventRecordOptionGroup group = recordGroupMap.computeIfAbsent(relation.getGroupId(), key -> {
|
||||
GaoEventRecordOptionGroup result = new GaoEventRecordOptionGroup();
|
||||
result.setGroupId(relation.getGroupId());
|
||||
result.setGroupName(relation.getGroupNameSnapshot());
|
||||
result.setSelectedOptionList(new ArrayList<>());
|
||||
return result;
|
||||
});
|
||||
GaoEventOption option = new GaoEventOption();
|
||||
option.setId(relation.getOptionId());
|
||||
option.setLabel(relation.getOptionLabelSnapshot());
|
||||
option.setSort(relation.getOptionSort());
|
||||
group.getSelectedOptionList().add(option);
|
||||
}
|
||||
for (GaoEventRecord record : TimiJava.safeIterable(recordList)) {
|
||||
if (record != null) {
|
||||
record.setSelectedOptionGroupList(new ArrayList<>(groupMap.getOrDefault(record.getId(), Map.of()).values()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void verify(String id) {
|
||||
TimiException.required(id, "not found record.id");
|
||||
Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_RECORD_VERIFY");
|
||||
try {
|
||||
logger.setContent(id);
|
||||
GaoEventRecord record = get(id);
|
||||
TimiException.required(record, "not found event record");
|
||||
TimiException.requiredTrue(record.getVerificationStatus() == GaoEventRecord.VerificationStatus.PENDING, "登记记录无需核销或已核销");
|
||||
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||
int updated = mapper.verify(id, loginUserId, Time.now(), loginUserId);
|
||||
TimiException.requiredTrue(0 < updated, "登记记录无需核销或已核销");
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
logger.setResult(id);
|
||||
} catch (TimiException e) {
|
||||
logger.setLevel(Logger.Level.WARN);
|
||||
logger.setException(e.getMessage());
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
logger.setLevel(Logger.Level.ERROR);
|
||||
logger.setException(TimiJava.serializeThrowable(e));
|
||||
log.error("核销 GAO 登记记录失败", e);
|
||||
throw new TimiException(TimiCode.ERROR, "核销 GAO 登记记录失败", e);
|
||||
} finally {
|
||||
loggerService.create(logger);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void create(GaoEventRecord record) {
|
||||
createInternal(record, false);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoEventRecord createOne(GaoEventRecord record) {
|
||||
return createInternal(record, false);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoEventRecord createPackagePurchaseRecord(GaoEventRecord record) {
|
||||
return createInternal(record, true);
|
||||
}
|
||||
|
||||
private GaoEventRecord createInternal(GaoEventRecord record, boolean packagePurchase) {
|
||||
TimiException.required(record, "not found record");
|
||||
|
||||
Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_RECORD_BATCH_CREATE");
|
||||
@@ -97,7 +188,16 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
GaoEvent event = eventService.get(record.getEventId());
|
||||
TimiException.required(event, "not found event");
|
||||
TimiException.required(event.getStoreId(), "not found event.storeId");
|
||||
if (packagePurchase) {
|
||||
TimiException.requiredTrue(event.getBizType() == GaoEvent.BizType.PACKAGE_PURCHASE, "购买事件类型无效");
|
||||
TimiException.requiredTrue(!Boolean.TRUE.equals(event.getRequireVerification()), "购买套餐事件不能要求核销");
|
||||
} else {
|
||||
TimiException.requiredTrue(event.getBizType() != GaoEvent.BizType.PACKAGE_PURCHASE, "购买套餐事件必须通过套餐购买接口登记");
|
||||
}
|
||||
eventService.attachOptionGroupList(List.of(event));
|
||||
List<GaoEventRecordOption> canonicalOptionList = resolveRecordOptionList(event, record.getSelectedOptionGroupList());
|
||||
verifyRecordValue(event, record.getNumberValue());
|
||||
verifyRecordAttachment(event, record.getAttachmentList());
|
||||
// 检查权限
|
||||
checkEventPermission(event);
|
||||
int registeredDateValue = dateValue(registeredAt);
|
||||
@@ -124,14 +224,19 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
dbRecord.setStoreId(event.getStoreId());
|
||||
dbRecord.setEventId(record.getEventId());
|
||||
dbRecord.setOperatorUserId(loginUserId);
|
||||
dbRecord.setUpdatedBy(loginUserId);
|
||||
dbRecord.setRemark(record.getRemark());
|
||||
dbRecord.setRegisteredAt(registeredAt);
|
||||
dbRecord.setRegisteredDateValue(registeredDateValue);
|
||||
dbRecord.setBusinessDayNo(businessDayNo);
|
||||
dbRecord.setPointValue(record.getPointValue());
|
||||
dbRecord.setNumberValue(record.getNumberValue());
|
||||
dbRecord.setVerificationStatus(Boolean.TRUE.equals(event.getRequireVerification())
|
||||
? GaoEventRecord.VerificationStatus.PENDING
|
||||
: GaoEventRecord.VerificationStatus.NOT_REQUIRED);
|
||||
dbRecord.setEarnedPointValue(0L);
|
||||
super.create(dbRecord);
|
||||
saveRecordOptionList(dbRecord.getId(), canonicalOptionList);
|
||||
// 附件
|
||||
if (i == 0) {
|
||||
// 第一个记录储存附件
|
||||
@@ -151,6 +256,7 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
}
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
logger.setResult(String.join(",", logIdList));
|
||||
return firstRecord;
|
||||
} catch (TimiException e) {
|
||||
logger.setLevel(Logger.Level.WARN);
|
||||
logger.setException(e.getMessage());
|
||||
@@ -168,11 +274,25 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void createBatch(List<GaoEventRecord> recordList) {
|
||||
Set<String> usedTempFileIdSet = new HashSet<>();
|
||||
for (GaoEventRecord record : TimiJava.safeIterable(recordList)) {
|
||||
TimiException.required(record, "not found record");
|
||||
prepareBatchAttachments(record, usedTempFileIdSet);
|
||||
create(record);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareBatchAttachments(GaoEventRecord record, Set<String> usedTempFileIdSet) {
|
||||
for (Attachment attachment : TimiJava.safeIterable(record.getAttachmentList())) {
|
||||
if (attachment == null || TimiJava.isEmpty(attachment.getTempFileId()) || usedTempFileIdSet.add(attachment.getTempFileId())) {
|
||||
continue;
|
||||
}
|
||||
Attachment clone = attachmentService.clone(attachment.getTempFileId());
|
||||
TimiException.required(clone.getId(), "登记照片临时附件复制失败");
|
||||
attachment.setTempFileId(clone.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void update(GaoEventRecord record) {
|
||||
@@ -190,9 +310,23 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
GaoEvent event = eventService.get(dbRecord.getEventId());
|
||||
TimiException.required(event, "not found event");
|
||||
TimiException.required(event.getStoreId(), "not found event.storeId");
|
||||
TimiException.requiredTrue(event.getBizType() != GaoEvent.BizType.PACKAGE_PURCHASE, "购买套餐事件登记不可通过通用接口修改");
|
||||
List<GaoEventRecordOption> canonicalOptionList = null;
|
||||
if (record.getSelectedOptionGroupList() != null) {
|
||||
eventService.attachOptionGroupList(List.of(event));
|
||||
canonicalOptionList = resolveRecordOptionList(event, record.getSelectedOptionGroupList());
|
||||
}
|
||||
verifyRecordAttachment(event, record.getAttachmentList());
|
||||
checkEventPermission(event);
|
||||
dbRecord.setRemark(record.getRemark());
|
||||
dbRecord.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||
super.update(dbRecord);
|
||||
if (canonicalOptionList != null) {
|
||||
GaoEventRecordOption optionExample = new GaoEventRecordOption();
|
||||
optionExample.setEventRecordId(record.getId());
|
||||
recordOptionMapper.deleteAllByExample(optionExample);
|
||||
saveRecordOptionList(record.getId(), canonicalOptionList);
|
||||
}
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_EVENT_RECORD, record.getId(), record.getAttachmentList());
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
logger.setResult(record.getId());
|
||||
@@ -219,7 +353,12 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
logger.setContent(id);
|
||||
GaoEventRecord record = get(id);
|
||||
TimiException.required(record, "not found event record");
|
||||
GaoEvent event = eventService.get(record.getEventId());
|
||||
TimiException.requiredTrue(event == null || event.getBizType() != GaoEvent.BizType.PACKAGE_PURCHASE, "购买套餐事件登记不可通过通用接口删除");
|
||||
pointTriggerService.revokeByEventRecord(record);
|
||||
GaoEventRecordOption optionExample = new GaoEventRecordOption();
|
||||
optionExample.setEventRecordId(record.getId());
|
||||
recordOptionMapper.deleteAllByExample(optionExample);
|
||||
attachmentService.deleteByBizId(Attachment.BizType.GAO_EVENT_RECORD, record.getId());
|
||||
super.delete(id);
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
@@ -281,6 +420,12 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
return mapper.sumNumberValueByRange(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoEventOptionStatView> optionStatByRange(GaoEventRecordPage page) {
|
||||
TimiException.required(page.getStoreId(), "not found page.storeId");
|
||||
return mapper.selectOptionStatByRange(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] export(GaoEventRecordPage page) throws IOException {
|
||||
TimiException.required(page.getStoreId(), "not found page.storeId");
|
||||
@@ -383,6 +528,85 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
||||
}
|
||||
}
|
||||
|
||||
private List<GaoEventRecordOption> resolveRecordOptionList(GaoEvent event, List<GaoEventRecordOptionGroup> selectedGroupList) {
|
||||
List<GaoEventOptionGroup> groupList = event.getOptionGroupList();
|
||||
Map<String, GaoEventOptionGroup> groupMap = new LinkedHashMap<>();
|
||||
for (GaoEventOptionGroup group : TimiJava.safeIterable(groupList)) {
|
||||
groupMap.put(group.getId(), group);
|
||||
}
|
||||
Map<String, GaoEventRecordOptionGroup> selectedMap = new LinkedHashMap<>();
|
||||
for (GaoEventRecordOptionGroup selectedGroup : TimiJava.safeIterable(selectedGroupList)) {
|
||||
TimiException.required(selectedGroup, "登记记录选项组不能为空");
|
||||
String groupId = selectedGroup.getGroupId();
|
||||
GaoEventOptionGroup group = groupMap.get(groupId);
|
||||
TimiException.required(group, "登记记录选项组不存在或不属于当前事件");
|
||||
TimiException.requiredTrue(selectedMap.put(groupId, selectedGroup) == null, "登记记录选项组不能重复");
|
||||
}
|
||||
|
||||
List<GaoEventRecordOption> result = new ArrayList<>();
|
||||
for (GaoEventOptionGroup group : TimiJava.safeIterable(groupList)) {
|
||||
GaoEventRecordOptionGroup selectedGroup = selectedMap.get(group.getId());
|
||||
List<GaoEventOption> selectedOptionList = selectedGroup == null ? List.of() : TimiJava.defaultIfNull(selectedGroup.getSelectedOptionList(), List.of());
|
||||
if (Boolean.TRUE.equals(group.getRequired())) {
|
||||
TimiException.requiredTrue(!selectedOptionList.isEmpty(), "请至少选择%s中的一个选项".formatted(group.getName()));
|
||||
}
|
||||
if (group.getSelectionMode() == GaoEventOptionGroup.SelectionMode.SINGLE) {
|
||||
TimiException.requiredTrue(selectedOptionList.size() < 2, "%s只能选择一个选项".formatted(group.getName()));
|
||||
}
|
||||
Map<String, GaoEventOption> optionMap = new LinkedHashMap<>();
|
||||
for (GaoEventOption option : TimiJava.safeIterable(group.getOptionList())) {
|
||||
optionMap.put(option.getId(), option);
|
||||
}
|
||||
Set<String> selectedOptionIdSet = new HashSet<>();
|
||||
int optionSort = 0;
|
||||
int groupSort = groupList.indexOf(group);
|
||||
for (GaoEventOption selectedOption : selectedOptionList) {
|
||||
TimiException.required(selectedOption, "登记记录选项不能为空");
|
||||
GaoEventOption option = optionMap.get(selectedOption.getId());
|
||||
TimiException.required(option, "登记记录选项不存在或不属于当前选项组");
|
||||
TimiException.requiredTrue(selectedOptionIdSet.add(option.getId()), "登记记录选项不能重复");
|
||||
GaoEventRecordOption recordOption = new GaoEventRecordOption();
|
||||
recordOption.setGroupId(group.getId());
|
||||
recordOption.setOptionId(option.getId());
|
||||
recordOption.setGroupSort(groupSort);
|
||||
recordOption.setOptionSort(option.getSort() == null ? optionSort : option.getSort());
|
||||
recordOption.setGroupNameSnapshot(group.getName());
|
||||
recordOption.setOptionLabelSnapshot(option.getLabel());
|
||||
result.add(recordOption);
|
||||
optionSort++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void saveRecordOptionList(String eventRecordId, List<GaoEventRecordOption> optionList) {
|
||||
for (GaoEventRecordOption option : TimiJava.safeIterable(optionList)) {
|
||||
GaoEventRecordOption recordOption = new GaoEventRecordOption();
|
||||
recordOption.setEventRecordId(eventRecordId);
|
||||
recordOption.setGroupId(option.getGroupId());
|
||||
recordOption.setOptionId(option.getOptionId());
|
||||
recordOption.setGroupSort(option.getGroupSort());
|
||||
recordOption.setOptionSort(option.getOptionSort());
|
||||
recordOption.setGroupNameSnapshot(option.getGroupNameSnapshot());
|
||||
recordOption.setOptionLabelSnapshot(option.getOptionLabelSnapshot());
|
||||
recordOptionMapper.insert(recordOption);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyRecordAttachment(GaoEvent event, List<Attachment> attachmentList) {
|
||||
if (!Boolean.TRUE.equals(event.getRequirePhoto())) {
|
||||
return;
|
||||
}
|
||||
boolean hasPhoto = false;
|
||||
for (Attachment attachment : TimiJava.safeIterable(attachmentList)) {
|
||||
if (attachment != null && (attachment.getAttachType() == null || GaoEventRecord.AttachType.PHOTO.name().equalsIgnoreCase(attachment.getAttachType()))) {
|
||||
hasPhoto = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TimiException.requiredTrue(hasPhoto, "该事件必须上传照片");
|
||||
}
|
||||
|
||||
private void verifyImmutableRecordFields(GaoEventRecord request, GaoEventRecord dbRecord) {
|
||||
TimiException.requiredTrue(request.getCustomerId() == null || request.getCustomerId().equals(dbRecord.getCustomerId()), "登记记录 customerId 创建后不可修改");
|
||||
TimiException.requiredTrue(request.getEventId() == null || request.getEventId().equals(dbRecord.getEventId()), "登记记录 eventId 创建后不可修改");
|
||||
|
||||
+205
-1
@@ -3,16 +3,24 @@ package com.imyeyu.api.modules.gao.service.implement;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOption;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventOptionGroup;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRoleRelation;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventOptionGroupMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventOptionMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventRoleRelationMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||
import com.imyeyu.api.modules.system.entity.Logger;
|
||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||
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 com.imyeyu.utils.Time;
|
||||
@@ -23,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -46,14 +55,25 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
private final LoggerService loggerService;
|
||||
|
||||
private final GaoEventMapper mapper;
|
||||
private final GaoEventOptionGroupMapper optionGroupMapper;
|
||||
private final GaoEventOptionMapper optionMapper;
|
||||
private final GaoEventRecordMapper eventRecordMapper;
|
||||
private final GaoEventRoleRelationMapper roleRelationMapper;
|
||||
private final GaoPackageConfigService packageConfigService;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<GaoEvent, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoEvent> pageWithoutDeleted(Page<GaoEvent> page) {
|
||||
PageResult<GaoEvent> result = new PageResult<>();
|
||||
result.setTotal(mapper.countPageWithoutDeleted(page));
|
||||
result.setList(mapper.selectPageWithoutDeleted(page));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void create(GaoEvent event) {
|
||||
@@ -66,15 +86,18 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
logger.setContent(jackson.writeValueAsString(event));
|
||||
|
||||
event.setStatus(TimiJava.defaultIfNull(event.getStatus(), GaoEvent.Status.DRAFT));
|
||||
event.setBizType(TimiJava.defaultIfNull(event.getBizType(), GaoEvent.BizType.GENERAL));
|
||||
event.setValueType(TimiJava.defaultIfNull(event.getValueType(), GaoEvent.ValueType.NONE));
|
||||
event.setRequireValue(TimiJava.defaultIfNull(event.getRequireValue(), false));
|
||||
event.setRequirePhoto(TimiJava.defaultIfNull(event.getRequirePhoto(), false));
|
||||
event.setRequireVerification(TimiJava.defaultIfNull(event.getRequireVerification(), false));
|
||||
event.setLimitType(TimiJava.defaultIfNull(event.getLimitType(), GaoEvent.LimitType.NONE));
|
||||
event.setSort(TimiJava.defaultIfNull(event.getSort(), 0));
|
||||
verifyEvent(event);
|
||||
super.create(event);
|
||||
|
||||
saveRoleRelation(event.getId(), event.getRoleCodeList());
|
||||
saveOptionGroupList(event.getId(), event.getOptionGroupList());
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
logger.setResult(event.getId());
|
||||
} catch (TimiException e) {
|
||||
@@ -100,12 +123,20 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
GaoEvent dbEvent = get(event.getId());
|
||||
TimiException.required(dbEvent, "not found event");
|
||||
TimiException.requiredTrue(dbEvent.getStoreId().equals(event.getStoreId()), "event.storeId invalid");
|
||||
TimiException.requiredTrue(dbEvent.getStatus() != GaoEvent.Status.DELETED, "已删除登记事件不可编辑");
|
||||
GaoEvent.ValueType oldValueType = dbEvent.getValueType();
|
||||
GaoEvent.ValueUnit oldValueUnit = dbEvent.getValueUnit();
|
||||
dbEvent.setName(event.getName());
|
||||
dbEvent.setDescription(event.getDescription());
|
||||
dbEvent.setColor(event.getColor());
|
||||
dbEvent.setStatus(event.getStatus());
|
||||
GaoEvent.BizType bizType = TimiJava.defaultIfNull(event.getBizType(), dbEvent.getBizType());
|
||||
TimiException.requiredTrue(bizType == TimiJava.defaultIfNull(dbEvent.getBizType(), GaoEvent.BizType.GENERAL), "事件业务类型创建后不可修改");
|
||||
dbEvent.setBizType(bizType);
|
||||
if (event.getPackageConfigId() != null && !event.getPackageConfigId().equals(dbEvent.getPackageConfigId())) {
|
||||
TimiException.requiredTrue(0 == eventRecordMapper.countByEventId(dbEvent.getId()), "已有登记记录的事件不能更换绑定套餐");
|
||||
dbEvent.setPackageConfigId(event.getPackageConfigId());
|
||||
}
|
||||
dbEvent.setValueType(TimiJava.defaultIfNull(event.getValueType(), oldValueType));
|
||||
if (event.getValueUnit() != null) {
|
||||
dbEvent.setValueUnit(event.getValueUnit());
|
||||
@@ -122,15 +153,22 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
dbEvent.setRequireValue(dbEvent.getRequireValue());
|
||||
}
|
||||
dbEvent.setRequirePhoto(event.getRequirePhoto());
|
||||
dbEvent.setRequireVerification(TimiJava.defaultIfNull(event.getRequireVerification(), dbEvent.getRequireVerification()));
|
||||
dbEvent.setLimitType(event.getLimitType());
|
||||
dbEvent.setLimitValue(event.getLimitValue());
|
||||
dbEvent.setBeginAt(event.getBeginAt());
|
||||
dbEvent.setEndAt(event.getEndAt());
|
||||
|
||||
if (event.getOptionGroupList() != null) {
|
||||
verifyOptionGroupList(event.getOptionGroupList());
|
||||
}
|
||||
verifyValueDefinitionChange(dbEvent, oldValueType, oldValueUnit);
|
||||
verifyEvent(dbEvent);
|
||||
mapper.update(dbEvent);
|
||||
saveRoleRelation(event.getId(), event.getRoleCodeList());
|
||||
if (event.getOptionGroupList() != null) {
|
||||
saveOptionGroupList(event.getId(), event.getOptionGroupList());
|
||||
}
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
logger.setResult(event.getId());
|
||||
} catch (TimiException e) {
|
||||
@@ -147,6 +185,33 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void updateOptionGroupList(String eventId, List<GaoEventOptionGroup> optionGroupList) {
|
||||
Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_OPTION_UPDATE");
|
||||
try {
|
||||
logger.setContent(jackson.writeValueAsString(optionGroupList));
|
||||
GaoEvent event = get(eventId);
|
||||
TimiException.required(event, "not found event");
|
||||
TimiException.requiredTrue(event.getStatus() != GaoEvent.Status.DELETED, "已删除登记事件不可编辑");
|
||||
verifyOptionGroupList(optionGroupList);
|
||||
saveOptionGroupList(eventId, optionGroupList);
|
||||
logger.setLevel(Logger.Level.INFO);
|
||||
logger.setResult(eventId);
|
||||
} catch (TimiException e) {
|
||||
logger.setLevel(Logger.Level.WARN);
|
||||
logger.setException(e.getMessage());
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
logger.setLevel(Logger.Level.ERROR);
|
||||
logger.setException(TimiJava.serializeThrowable(e));
|
||||
log.error("update event option error", e);
|
||||
throw new TimiException(TimiCode.ERROR, "update event option error", e);
|
||||
} finally {
|
||||
loggerService.create(logger);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
@@ -201,7 +266,36 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
@Override
|
||||
public List<GaoEvent> listValid(String storeId) {
|
||||
TimiException.required(storeId, "not found storeId");
|
||||
return mapper.selectValidList(storeId, Time.now());
|
||||
List<GaoEvent> eventList = mapper.selectValidList(storeId, Time.now());
|
||||
attachOptionGroupList(eventList);
|
||||
return eventList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachOptionGroupList(Collection<GaoEvent> eventList) {
|
||||
List<GaoEvent> validEventList = new ArrayList<>();
|
||||
for (GaoEvent event : TimiJava.safeIterable(eventList)) {
|
||||
if (event != null && TimiJava.isNotEmpty(event.getId())) {
|
||||
validEventList.add(event);
|
||||
}
|
||||
}
|
||||
if (validEventList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> eventIdList = validEventList.stream().map(GaoEvent::getId).toList();
|
||||
List<GaoEventOptionGroup> groupList = optionGroupMapper.selectAllByEventIdList(eventIdList);
|
||||
List<String> groupIdList = groupList.stream().map(GaoEventOptionGroup::getId).filter(TimiJava::isNotEmpty).toList();
|
||||
List<GaoEventOption> optionList = optionMapper.selectAllByGroupIdList(groupIdList);
|
||||
Map<String, List<GaoEventOption>> optionMap = optionList.stream().collect(Collectors.groupingBy(GaoEventOption::getGroupId));
|
||||
Map<String, List<GaoEventOptionGroup>> groupMap = new HashMap<>();
|
||||
for (GaoEventOptionGroup group : groupList) {
|
||||
group.setOptionList(optionMap.getOrDefault(group.getId(), List.of()));
|
||||
groupMap.computeIfAbsent(group.getEventId(), key -> new ArrayList<>()).add(group);
|
||||
}
|
||||
for (GaoEvent event : validEventList) {
|
||||
event.setOptionGroupList(groupMap.getOrDefault(event.getId(), List.of()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -227,6 +321,16 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
}
|
||||
|
||||
private void verifyEvent(GaoEvent event) {
|
||||
event.setBizType(TimiJava.defaultIfNull(event.getBizType(), GaoEvent.BizType.GENERAL));
|
||||
if (event.getBizType() == GaoEvent.BizType.PACKAGE_PURCHASE) {
|
||||
TimiException.requiredTrue(!Boolean.TRUE.equals(event.getRequireVerification()), "购买套餐事件不能要求核销");
|
||||
TimiException.required(event.getPackageConfigId(), "购买套餐事件必须绑定套餐配置");
|
||||
GaoPackageConfig packageConfig = packageConfigService.get(event.getPackageConfigId());
|
||||
TimiException.required(packageConfig, "购买套餐事件绑定的套餐配置不存在");
|
||||
TimiException.requiredTrue(event.getStoreId().equals(packageConfig.getStoreId()), "购买事件和套餐配置不属于同一门店");
|
||||
} else {
|
||||
TimiException.requiredNull(event.getPackageConfigId(), "普通事件不能绑定套餐配置");
|
||||
}
|
||||
GaoEvent.ValueType valueType = TimiJava.defaultIfNull(event.getValueType(), GaoEvent.ValueType.NONE);
|
||||
if (valueType == GaoEvent.ValueType.NONE) {
|
||||
TimiException.requiredNull(event.getValueUnit(), "NONE 事件不能配置数值单位");
|
||||
@@ -240,6 +344,106 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
|
||||
if (event.getBeginAt() != null || event.getEndAt() != null) {
|
||||
TimiException.requiredTrue(event.getBeginAt() == null || event.getEndAt() == null || event.getBeginAt() <= event.getEndAt(), "invalid beginAt or endAt");
|
||||
}
|
||||
verifyOptionGroupList(event.getOptionGroupList());
|
||||
}
|
||||
|
||||
private void verifyOptionGroupList(List<GaoEventOptionGroup> groupList) {
|
||||
Set<String> groupIdSet = new HashSet<>();
|
||||
Set<String> groupNameSet = new HashSet<>();
|
||||
for (GaoEventOptionGroup group : TimiJava.safeIterable(groupList)) {
|
||||
TimiException.required(group, "登记事件选项组不能为空");
|
||||
String groupName = group.getName() == null ? "" : group.getName().trim();
|
||||
TimiException.requiredTrue(!groupName.isEmpty(), "登记事件选项组名称不能为空");
|
||||
TimiException.requiredTrue(groupNameSet.add(groupName), "登记事件选项组名称不能重复");
|
||||
if (TimiJava.isNotEmpty(group.getId())) {
|
||||
TimiException.requiredTrue(groupIdSet.add(group.getId()), "登记事件选项组 ID 不能重复");
|
||||
}
|
||||
TimiException.required(group.getSelectionMode(), "登记事件选项组必须配置选择模式");
|
||||
List<GaoEventOption> optionList = group.getOptionList();
|
||||
TimiException.requiredTrue(optionList != null && !optionList.isEmpty(), "登记事件选项组至少配置一个选项");
|
||||
Set<String> optionIdSet = new HashSet<>();
|
||||
Set<String> optionNameSet = new HashSet<>();
|
||||
for (GaoEventOption option : TimiJava.safeIterable(optionList)) {
|
||||
TimiException.required(option, "登记事件选项不能为空");
|
||||
String label = option.getLabel() == null ? "" : option.getLabel().trim();
|
||||
TimiException.requiredTrue(!label.isEmpty(), "登记事件选项名称不能为空");
|
||||
TimiException.requiredTrue(optionNameSet.add(label), "同一选项组内选项名称不能重复");
|
||||
if (TimiJava.isNotEmpty(option.getId())) {
|
||||
TimiException.requiredTrue(optionIdSet.add(option.getId()), "登记事件选项 ID 不能重复");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveOptionGroupList(String eventId, List<GaoEventOptionGroup> groupList) {
|
||||
List<GaoEventOptionGroup> dbGroupList = optionGroupMapper.selectAllByEventIdList(List.of(eventId));
|
||||
Map<String, GaoEventOptionGroup> dbGroupMap = dbGroupList.stream().collect(Collectors.toMap(GaoEventOptionGroup::getId, Function.identity()));
|
||||
Set<String> retainedGroupIdSet = new HashSet<>();
|
||||
int groupSort = 0;
|
||||
for (GaoEventOptionGroup group : TimiJava.safeIterable(groupList)) {
|
||||
String groupId = group.getId();
|
||||
GaoEventOptionGroup dbGroup;
|
||||
if (TimiJava.isNotEmpty(groupId)) {
|
||||
dbGroup = dbGroupMap.get(groupId);
|
||||
TimiException.required(dbGroup, "登记事件选项组不属于当前事件");
|
||||
dbGroup.setName(group.getName().trim());
|
||||
dbGroup.setSelectionMode(group.getSelectionMode());
|
||||
dbGroup.setRequired(TimiJava.defaultIfNull(group.getRequired(), false));
|
||||
dbGroup.setSort(groupSort);
|
||||
optionGroupMapper.update(dbGroup);
|
||||
} else {
|
||||
dbGroup = group;
|
||||
dbGroup.setEventId(eventId);
|
||||
dbGroup.setName(group.getName().trim());
|
||||
dbGroup.setRequired(TimiJava.defaultIfNull(group.getRequired(), false));
|
||||
dbGroup.setSort(groupSort);
|
||||
optionGroupMapper.insert(dbGroup);
|
||||
}
|
||||
groupId = dbGroup.getId();
|
||||
retainedGroupIdSet.add(groupId);
|
||||
saveOptionList(groupId, group.getOptionList());
|
||||
groupSort++;
|
||||
}
|
||||
for (GaoEventOptionGroup dbGroup : dbGroupList) {
|
||||
if (retainedGroupIdSet.contains(dbGroup.getId())) {
|
||||
continue;
|
||||
}
|
||||
GaoEventOption optionExample = new GaoEventOption();
|
||||
optionExample.setGroupId(dbGroup.getId());
|
||||
optionMapper.deleteAllByExample(optionExample);
|
||||
optionGroupMapper.delete(dbGroup.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveOptionList(String groupId, List<GaoEventOption> optionList) {
|
||||
List<GaoEventOption> dbOptionList = optionMapper.selectAllByGroupIdList(List.of(groupId));
|
||||
Map<String, GaoEventOption> dbOptionMap = dbOptionList.stream().collect(Collectors.toMap(GaoEventOption::getId, Function.identity()));
|
||||
Set<String> retainedOptionIdSet = new HashSet<>();
|
||||
int optionSort = 0;
|
||||
for (GaoEventOption option : TimiJava.safeIterable(optionList)) {
|
||||
String optionId = option.getId();
|
||||
GaoEventOption dbOption;
|
||||
if (TimiJava.isNotEmpty(optionId)) {
|
||||
dbOption = dbOptionMap.get(optionId);
|
||||
TimiException.required(dbOption, "登记事件选项不属于当前选项组");
|
||||
dbOption.setLabel(option.getLabel().trim());
|
||||
dbOption.setSort(optionSort);
|
||||
optionMapper.update(dbOption);
|
||||
} else {
|
||||
dbOption = option;
|
||||
dbOption.setGroupId(groupId);
|
||||
dbOption.setLabel(option.getLabel().trim());
|
||||
dbOption.setSort(optionSort);
|
||||
optionMapper.insert(dbOption);
|
||||
}
|
||||
retainedOptionIdSet.add(dbOption.getId());
|
||||
optionSort++;
|
||||
}
|
||||
for (GaoEventOption dbOption : dbOptionList) {
|
||||
if (!retainedOptionIdSet.contains(dbOption.getId())) {
|
||||
optionMapper.delete(dbOption.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyValueDefinitionChange(GaoEvent event, GaoEvent.ValueType oldValueType, GaoEvent.ValueUnit oldValueUnit) {
|
||||
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
package com.imyeyu.api.modules.gao.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoFundRecordMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageDeliveryService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundActionRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundConfirmNoRefundRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRecordPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundReceiveRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRefundRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoFundRevokeRequest;
|
||||
import com.imyeyu.api.modules.system.entity.Logger;
|
||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
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.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 通用资金流水服务实现
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GaoFundRecordServiceImplement extends AbstractEntityService<GaoFundRecord, String> implements GaoFundRecordService {
|
||||
|
||||
private final GaoFundRecordMapper mapper;
|
||||
private final GaoCustomerPackageService customerPackageService;
|
||||
private final GaoPackageDeliveryService deliveryService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final UserLoginService userLoginService;
|
||||
private final LoggerService loggerService;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<GaoFundRecord, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
/// 资金流水必须通过专用操作创建
|
||||
///
|
||||
/// @param record 资金流水
|
||||
@Override
|
||||
public void create(GaoFundRecord record) {
|
||||
TimiException.requiredTrue(false, "资金流水必须通过专用接口创建");
|
||||
}
|
||||
|
||||
/// 资金流水不允许通用修改
|
||||
///
|
||||
/// @param record 资金流水
|
||||
@Override
|
||||
public void update(GaoFundRecord record) {
|
||||
TimiException.requiredTrue(false, "资金流水不允许通用修改");
|
||||
}
|
||||
|
||||
/// 资金流水不允许删除
|
||||
///
|
||||
/// @param id 资金流水 ID
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
TimiException.requiredTrue(false, "资金流水不允许删除");
|
||||
}
|
||||
|
||||
/// 资金流水不允许物理删除
|
||||
///
|
||||
/// @param id 资金流水 ID
|
||||
@Override
|
||||
public void destroy(String id) {
|
||||
TimiException.requiredTrue(false, "资金流水不允许物理删除");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoFundRecord> pageByQuery(GaoFundRecordPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
PageResult<GaoFundRecord> result = new PageResult<>();
|
||||
result.setTotal(mapper.countByQuery(page));
|
||||
result.setList(mapper.selectByQuery(page));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoFundRecord> listByCustomerPackageId(String customerPackageId) {
|
||||
TimiException.required(customerPackageId, "not found customerPackageId");
|
||||
return mapper.selectByBizId(customerPackageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countPendingReceiveByBizId(String bizId) {
|
||||
return mapper.countPendingReceiveByBizId(bizId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoFundRecord get(String id) {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoFundRecord receive(GaoFundReceiveRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_FUND_RECEIVE_CREATE", request, () -> receiveInternal(request));
|
||||
}
|
||||
|
||||
private GaoFundRecord receiveInternal(GaoFundReceiveRequest request) {
|
||||
TimiException.required(request, "not found receive request");
|
||||
TimiException.required(request.getRequestNo(), "not found requestNo");
|
||||
request.setRequestNo(request.getRequestNo().trim());
|
||||
TimiException.required(request.getCustomerPackageId(), "not found customerPackageId");
|
||||
TimiException.required(request.getAmount(), "not found amount");
|
||||
TimiException.requiredTrue(0 < request.getAmount(), "收款金额必须大于 0");
|
||||
GaoCustomerPackage packageEntity = lockPackage(request.getCustomerPackageId());
|
||||
GaoFundRecord existing = mapper.selectByRequestNo(packageEntity.getStoreId(), request.getRequestNo());
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(existing.getBizId().equals(packageEntity.getId())
|
||||
&& existing.getOperationType() == GaoFundRecord.OperationType.RECEIVE
|
||||
&& existing.getAmount().equals(request.getAmount())
|
||||
&& (request.getOccurredAt() == null || request.getOccurredAt().equals(existing.getOccurredAt()))
|
||||
&& sameText(existing.getRemark(), request.getRemark()), "requestNo 已被其他资金操作使用");
|
||||
return get(existing.getId());
|
||||
}
|
||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "收款必须上传照片");
|
||||
checkPackageReceivable(packageEntity);
|
||||
TimiException.requiredTrue(safeLong(packageEntity.getPaidAmount()) + safeLong(packageEntity.getPendingPaidAmount()) + request.getAmount() <= safeLong(packageEntity.getSaleAmount()), "累计收款不能超过成交价");
|
||||
GaoFundRecord record = newRecord(packageEntity, GaoFundRecord.OperationType.RECEIVE, request.getRequestNo(), request.getAmount(), request.getOccurredAt(), request.getRemark());
|
||||
record.setStatus(GaoFundRecord.Status.PENDING);
|
||||
super.create(record);
|
||||
TimiException.requiredTrue(0 < customerPackageService.updatePendingPaidAmountDelta(packageEntity.getId(), request.getAmount()), "待核销收款更新失败");
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_FUND_RECORD, record.getId(), request.getAttachmentList());
|
||||
return record;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoFundRecord verify(String fundRecordId) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_FUND_RECEIVE_VERIFY", fundRecordId, () -> verifyInternal(fundRecordId));
|
||||
}
|
||||
|
||||
private GaoFundRecord verifyInternal(String fundRecordId) {
|
||||
GaoFundRecord record = mapper.selectForUpdate(fundRecordId);
|
||||
TimiException.required(record, "not found fund record");
|
||||
storeScopeService.checkStoreId(record.getStoreId());
|
||||
TimiException.requiredTrue(record.getOperationType() == GaoFundRecord.OperationType.RECEIVE && record.getStatus() == GaoFundRecord.Status.PENDING, "收款记录不是待核销状态");
|
||||
GaoCustomerPackage packageEntity = lockPackage(record.getBizId());
|
||||
TimiException.requiredTrue(0 < customerPackageService.updatePendingPaidAmountDelta(packageEntity.getId(), -record.getAmount()), "待核销收款更新失败");
|
||||
TimiException.requiredTrue(0 < customerPackageService.updatePaidAmountDelta(packageEntity.getId(), record.getAmount()), "有效收款更新失败");
|
||||
String userId = userLoginService.getRequireLoginUserId();
|
||||
TimiException.requiredTrue(0 < mapper.updateStatusIf(record.getId(), GaoFundRecord.Status.PENDING, GaoFundRecord.Status.VERIFIED, userId, Time.now(), record.getReason()), "收款记录状态已变化");
|
||||
updatePaymentStatus(packageEntity, safeLong(packageEntity.getPaidAmount()) + record.getAmount(), safeLong(packageEntity.getPendingPaidAmount()) - record.getAmount());
|
||||
record.setStatus(GaoFundRecord.Status.VERIFIED);
|
||||
record.setVerifierUserId(userId);
|
||||
record.setVerifiedAt(Time.now());
|
||||
return record;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoFundRecord reject(GaoFundActionRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_FUND_RECEIVE_REJECT", request, () -> releasePending(request, GaoFundRecord.Status.REJECTED));
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoFundRecord cancel(GaoFundActionRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_FUND_RECEIVE_CANCEL", request, () -> {
|
||||
TimiException.required(request, "not found fund action request");
|
||||
TimiException.required(request.getFundRecordId(), "not found fundRecordId");
|
||||
GaoFundRecord record = mapper.selectForUpdate(request.getFundRecordId());
|
||||
TimiException.required(record, "not found fund record");
|
||||
TimiException.requiredTrue(userLoginService.getRequireLoginUserId().equals(record.getOperatorUserId()), "只能撤销自己提交的收款");
|
||||
return releasePending(request, GaoFundRecord.Status.CANCELLED);
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoFundRecord refund(GaoFundRefundRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_FUND_REFUND", request, () -> refundInternal(request));
|
||||
}
|
||||
|
||||
private GaoFundRecord refundInternal(GaoFundRefundRequest request) {
|
||||
TimiException.required(request, "not found refund request");
|
||||
TimiException.required(request.getRequestNo(), "not found requestNo");
|
||||
request.setRequestNo(request.getRequestNo().trim());
|
||||
TimiException.required(request.getCustomerPackageId(), "not found customerPackageId");
|
||||
TimiException.required(request.getAmount(), "not found amount");
|
||||
TimiException.requiredTrue(0 < request.getAmount(), "退款金额必须大于 0");
|
||||
TimiException.requiredTrue(hasReason(request.getReason()), "退款原因不能为空");
|
||||
GaoCustomerPackage packageEntity = lockPackage(request.getCustomerPackageId());
|
||||
GaoFundRecord existing = mapper.selectByRequestNo(packageEntity.getStoreId(), request.getRequestNo());
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(existing.getBizId().equals(packageEntity.getId())
|
||||
&& existing.getOperationType() == GaoFundRecord.OperationType.REFUND
|
||||
&& existing.getAmount().equals(request.getAmount())
|
||||
&& (request.getOccurredAt() == null || request.getOccurredAt().equals(existing.getOccurredAt()))
|
||||
&& sameText(existing.getReason(), request.getReason().trim())
|
||||
&& sameText(existing.getRemark(), request.getRemark()), "requestNo 已被其他资金操作使用");
|
||||
return get(existing.getId());
|
||||
}
|
||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "退款必须上传照片");
|
||||
checkRefundable(packageEntity);
|
||||
TimiException.requiredTrue(request.getAmount() <= safeLong(packageEntity.getPaidAmount()), "退款金额不能超过累计有效收款");
|
||||
GaoFundRecord record = newRecord(packageEntity, GaoFundRecord.OperationType.REFUND, request.getRequestNo(), request.getAmount(), request.getOccurredAt(), request.getRemark());
|
||||
record.setStatus(GaoFundRecord.Status.VERIFIED);
|
||||
record.setReason(request.getReason().trim());
|
||||
super.create(record);
|
||||
packageEntity.setRefundedAmount(request.getAmount());
|
||||
packageEntity.setRefundStatus(GaoCustomerPackage.RefundStatus.REFUNDED);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_FUND_RECORD, record.getId(), request.getAttachmentList());
|
||||
return record;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoFundRecord confirmNoRefund(GaoFundConfirmNoRefundRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_FUND_CONFIRM_NO_REFUND", request, () -> confirmNoRefundInternal(request));
|
||||
}
|
||||
|
||||
private GaoFundRecord confirmNoRefundInternal(GaoFundConfirmNoRefundRequest request) {
|
||||
TimiException.required(request, "not found no refund request");
|
||||
TimiException.required(request.getRequestNo(), "not found requestNo");
|
||||
request.setRequestNo(request.getRequestNo().trim());
|
||||
TimiException.required(request.getCustomerPackageId(), "not found customerPackageId");
|
||||
TimiException.requiredTrue(hasReason(request.getReason()), "确认无需退款原因不能为空");
|
||||
GaoCustomerPackage packageEntity = lockPackage(request.getCustomerPackageId());
|
||||
GaoFundRecord existing = mapper.selectByRequestNo(packageEntity.getStoreId(), request.getRequestNo());
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(existing.getBizId().equals(packageEntity.getId())
|
||||
&& existing.getOperationType() == GaoFundRecord.OperationType.CONFIRM_NO_REFUND
|
||||
&& (request.getOccurredAt() == null || request.getOccurredAt().equals(existing.getOccurredAt()))
|
||||
&& sameText(existing.getReason(), request.getReason().trim())
|
||||
&& sameText(existing.getRemark(), request.getRemark()), "requestNo 已被其他资金操作使用");
|
||||
return get(existing.getId());
|
||||
}
|
||||
checkRefundable(packageEntity);
|
||||
GaoFundRecord record = newRecord(packageEntity, GaoFundRecord.OperationType.CONFIRM_NO_REFUND, request.getRequestNo(), 0L, request.getOccurredAt(), request.getRemark());
|
||||
record.setStatus(GaoFundRecord.Status.VERIFIED);
|
||||
record.setReason(request.getReason().trim());
|
||||
super.create(record);
|
||||
packageEntity.setRefundedAmount(0L);
|
||||
packageEntity.setRefundStatus(GaoCustomerPackage.RefundStatus.REFUNDED);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoFundRecord revoke(GaoFundRevokeRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_FUND_REVOKE", request, () -> revokeInternal(request));
|
||||
}
|
||||
|
||||
private GaoFundRecord revokeInternal(GaoFundRevokeRequest request) {
|
||||
TimiException.required(request, "not found fund revoke request");
|
||||
TimiException.required(request.getRequestNo(), "not found requestNo");
|
||||
request.setRequestNo(request.getRequestNo().trim());
|
||||
TimiException.required(request.getFundRecordId(), "not found fundRecordId");
|
||||
TimiException.requiredTrue(hasReason(request.getReason()), "资金冲正原因不能为空");
|
||||
GaoFundRecord source = mapper.selectForUpdate(request.getFundRecordId());
|
||||
TimiException.required(source, "not found fund record");
|
||||
storeScopeService.checkStoreId(source.getStoreId());
|
||||
GaoFundRecord existing = mapper.selectByRequestNo(source.getStoreId(), request.getRequestNo());
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(existing.getOperationType() == GaoFundRecord.OperationType.REVOKE
|
||||
&& source.getId().equals(existing.getSourceRecordId()), "requestNo 已被其他资金操作使用");
|
||||
return get(existing.getId());
|
||||
}
|
||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "资金冲正必须上传照片");
|
||||
TimiException.requiredTrue(source.getStatus() == GaoFundRecord.Status.VERIFIED && source.getOperationType() != GaoFundRecord.OperationType.REVOKE, "只有已生效资金记录可以冲正");
|
||||
TimiException.requiredNull(mapper.selectRevokeBySourceRecordId(source.getId()), "该资金记录已经冲正");
|
||||
GaoCustomerPackage packageEntity = lockPackage(source.getBizId());
|
||||
if (source.getOperationType() == GaoFundRecord.OperationType.RECEIVE) {
|
||||
TimiException.requiredTrue(packageEntity.getRefundStatus() != GaoCustomerPackage.RefundStatus.REFUNDED, "退款完成后不能冲正相关收款");
|
||||
long paidAfter = safeLong(packageEntity.getPaidAmount()) - source.getAmount();
|
||||
if (0 < deliveryService.countVerifiedByCustomerPackageId(packageEntity.getId())) {
|
||||
TimiException.requiredTrue(packageEntity.getSaleAmount() <= paidAfter, "存在已核销发放,冲正收款后套餐仍须付清");
|
||||
}
|
||||
TimiException.requiredTrue(0 < customerPackageService.updatePaidAmountDelta(packageEntity.getId(), -source.getAmount()), "有效收款冲正失败");
|
||||
packageEntity.setPaidAmount(paidAfter);
|
||||
updatePaymentStatus(packageEntity, paidAfter, packageEntity.getPendingPaidAmount());
|
||||
} else {
|
||||
TimiException.requiredTrue(packageEntity.getRefundStatus() == GaoCustomerPackage.RefundStatus.REFUNDED, "资金记录退款状态无效");
|
||||
packageEntity.setRefundedAmount(0L);
|
||||
packageEntity.setRefundStatus(GaoCustomerPackage.RefundStatus.PENDING);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
}
|
||||
GaoFundRecord revoke = newRecord(packageEntity, GaoFundRecord.OperationType.REVOKE, request.getRequestNo(), source.getAmount(), request.getOccurredAt(), request.getRemark());
|
||||
revoke.setStatus(GaoFundRecord.Status.VERIFIED);
|
||||
revoke.setSourceRecordId(source.getId());
|
||||
revoke.setReason(request.getReason().trim());
|
||||
super.create(revoke);
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_FUND_RECORD, revoke.getId(), request.getAttachmentList());
|
||||
return revoke;
|
||||
}
|
||||
|
||||
private GaoFundRecord releasePending(GaoFundActionRequest request, GaoFundRecord.Status targetStatus) {
|
||||
TimiException.required(request, "not found fund action request");
|
||||
TimiException.required(request.getFundRecordId(), "not found fundRecordId");
|
||||
TimiException.requiredTrue(hasReason(request.getReason()), "拒绝或撤销原因不能为空");
|
||||
GaoFundRecord record = mapper.selectForUpdate(request.getFundRecordId());
|
||||
TimiException.required(record, "not found fund record");
|
||||
storeScopeService.checkStoreId(record.getStoreId());
|
||||
TimiException.requiredTrue(record.getOperationType() == GaoFundRecord.OperationType.RECEIVE && record.getStatus() == GaoFundRecord.Status.PENDING, "收款记录不是待核销状态");
|
||||
GaoCustomerPackage packageEntity = lockPackage(record.getBizId());
|
||||
TimiException.requiredTrue(0 < customerPackageService.updatePendingPaidAmountDelta(packageEntity.getId(), -record.getAmount()), "待核销收款更新失败");
|
||||
String userId = userLoginService.getRequireLoginUserId();
|
||||
TimiException.requiredTrue(0 < mapper.updateStatusIf(record.getId(), GaoFundRecord.Status.PENDING, targetStatus, userId, Time.now(), request.getReason().trim()), "收款记录状态已变化");
|
||||
record.setStatus(targetStatus);
|
||||
record.setVerifierUserId(userId);
|
||||
record.setVerifiedAt(Time.now());
|
||||
record.setReason(request.getReason().trim());
|
||||
return record;
|
||||
}
|
||||
|
||||
private GaoCustomerPackage lockPackage(String id) {
|
||||
GaoCustomerPackage result = customerPackageService.getForUpdate(id);
|
||||
TimiException.required(result, "not found customer package");
|
||||
storeScopeService.checkStoreId(result.getStoreId());
|
||||
return result;
|
||||
}
|
||||
|
||||
private void checkPackageReceivable(GaoCustomerPackage packageEntity) {
|
||||
TimiException.requiredTrue(packageEntity.getStatus() != GaoCustomerPackage.Status.CANCELLED, "已取消套餐不能收款");
|
||||
TimiException.requiredTrue(packageEntity.getRefundStatus() != GaoCustomerPackage.RefundStatus.REFUNDED, "已完成退款套餐不能收款");
|
||||
}
|
||||
|
||||
private void checkRefundable(GaoCustomerPackage packageEntity) {
|
||||
TimiException.requiredTrue(packageEntity.getStatus() == GaoCustomerPackage.Status.CANCELLED, "只有已取消套餐可以退款");
|
||||
TimiException.requiredTrue(packageEntity.getRefundStatus() == GaoCustomerPackage.RefundStatus.PENDING, "当前套餐不在待退款状态");
|
||||
}
|
||||
|
||||
private GaoFundRecord newRecord(GaoCustomerPackage packageEntity, GaoFundRecord.OperationType operationType, String requestNo, Long amount, Long occurredAt, String remark) {
|
||||
GaoFundRecord record = new GaoFundRecord();
|
||||
record.setStoreId(packageEntity.getStoreId());
|
||||
record.setCustomerId(packageEntity.getCustomerId());
|
||||
record.setBizType(GaoFundRecord.BizType.CUSTOMER_PACKAGE);
|
||||
record.setBizId(packageEntity.getId());
|
||||
record.setOperationType(operationType);
|
||||
record.setAmount(amount);
|
||||
record.setRequestNo(requestNo);
|
||||
record.setOperatorUserId(userLoginService.getRequireLoginUserId());
|
||||
record.setOccurredAt(TimiJava.defaultIfNull(occurredAt, Time.now()));
|
||||
record.setRemark(remark);
|
||||
return record;
|
||||
}
|
||||
|
||||
private void updatePaymentStatus(GaoCustomerPackage packageEntity, long paidAmount, long pendingAmount) {
|
||||
packageEntity.setPaidAmount(paidAmount);
|
||||
packageEntity.setPendingPaidAmount(pendingAmount);
|
||||
packageEntity.setPaymentStatus(safeLong(packageEntity.getSaleAmount()) == 0 || paidAmount >= safeLong(packageEntity.getSaleAmount())
|
||||
? GaoCustomerPackage.PaymentStatus.PAID
|
||||
: (0 < paidAmount ? GaoCustomerPackage.PaymentStatus.PARTIALLY_PAID : GaoCustomerPackage.PaymentStatus.UNPAID));
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
}
|
||||
|
||||
private boolean hasAttachment(List<Attachment> attachmentList) {
|
||||
for (Attachment attachment : TimiJava.safeIterable(attachmentList)) {
|
||||
if (attachment != null && (attachment.getAttachType() == null || "PHOTO".equalsIgnoreCase(attachment.getAttachType()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasReason(String reason) {
|
||||
return reason != null && !reason.trim().isEmpty();
|
||||
}
|
||||
|
||||
private boolean sameText(String left, String right) {
|
||||
return left == null ? right == null : left.equals(right);
|
||||
}
|
||||
|
||||
private long safeLong(Long value) {
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
package com.imyeyu.api.modules.gao.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||
import com.imyeyu.api.modules.system.entity.Logger;
|
||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigItemMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoEventMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
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.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/// GAO 套餐配置服务实现
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoPackageConfig, String> implements GaoPackageConfigService {
|
||||
|
||||
private final GaoPackageConfigMapper mapper;
|
||||
private final GaoPackageConfigItemMapper itemMapper;
|
||||
private final GaoEventMapper eventMapper;
|
||||
private final UserLoginService userLoginService;
|
||||
private final LoggerService loggerService;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<GaoPackageConfig, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoPackageConfig> pageByQuery(GaoPackageConfigPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
PageResult<GaoPackageConfig> result = new PageResult<>();
|
||||
result.setTotal(mapper.countByQuery(page));
|
||||
result.setList(mapper.selectByQuery(page));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoPackageConfig> listValid(String storeId) {
|
||||
TimiException.required(storeId, "not found storeId");
|
||||
return mapper.selectValidList(storeId, Time.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, GaoPackageConfig> mapByIdList(Collection<String> idList) {
|
||||
if (idList == null || idList.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return mapper.selectByIdList(idList).stream()
|
||||
.collect(java.util.stream.Collectors.toMap(GaoPackageConfig::getId, item -> item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoPackageConfigItem> listItemByPackageConfigId(String packageConfigId) {
|
||||
TimiException.required(packageConfigId, "not found packageConfigId");
|
||||
return itemMapper.selectByPackageConfigId(packageConfigId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<GaoPackageConfigItem>> mapItemListByPackageConfigIdList(Collection<String> packageConfigIdList) {
|
||||
if (packageConfigIdList == null || packageConfigIdList.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return itemMapper.selectByPackageConfigIdList(packageConfigIdList).stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(GaoPackageConfigItem::getPackageConfigId));
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void create(GaoPackageConfig config) {
|
||||
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_CREATE", config, () -> {
|
||||
createInternal(config);
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
private void createInternal(GaoPackageConfig config) {
|
||||
TimiException.required(config, "not found package config");
|
||||
TimiException.required(config.getStoreId(), "not found packageConfig.storeId");
|
||||
TimiException.required(config.getItemList(), "套餐至少配置一个产品");
|
||||
prepareConfig(config, null);
|
||||
config.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||
super.create(config);
|
||||
saveItems(config.getId(), config.getItemList());
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void update(GaoPackageConfig request) {
|
||||
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_UPDATE", request, () -> {
|
||||
updateInternal(request);
|
||||
return request;
|
||||
});
|
||||
}
|
||||
|
||||
private void updateInternal(GaoPackageConfig request) {
|
||||
TimiException.required(request, "not found package config");
|
||||
TimiException.required(request.getId(), "not found packageConfig.id");
|
||||
GaoPackageConfig dbConfig = get(request.getId());
|
||||
TimiException.required(dbConfig, "not found package config");
|
||||
TimiException.requiredTrue(request.getStoreId() == null || request.getStoreId().equals(dbConfig.getStoreId()), "packageConfig.storeId invalid");
|
||||
dbConfig.setName(request.getName());
|
||||
dbConfig.setDescription(request.getDescription());
|
||||
dbConfig.setStandardAmount(TimiJava.defaultIfNull(request.getStandardAmount(), dbConfig.getStandardAmount()));
|
||||
dbConfig.setStatus(TimiJava.defaultIfNull(request.getStatus(), dbConfig.getStatus()));
|
||||
dbConfig.setBeginAt(request.getBeginAt());
|
||||
dbConfig.setEndAt(request.getEndAt());
|
||||
dbConfig.setSort(request.getSort());
|
||||
prepareConfig(dbConfig, dbConfig.getId());
|
||||
dbConfig.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||
super.update(dbConfig);
|
||||
if (request.getItemList() != null) {
|
||||
verifyItems(request.getItemList());
|
||||
GaoPackageConfigItem example = new GaoPackageConfigItem();
|
||||
example.setPackageConfigId(dbConfig.getId());
|
||||
itemMapper.deleteAllByExample(example);
|
||||
saveItems(dbConfig.getId(), request.getItemList());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_DELETE", id, () -> {
|
||||
GaoPackageConfig config = get(id);
|
||||
TimiException.required(config, "not found package config");
|
||||
TimiException.requiredTrue(0 == eventMapper.countByPackageConfigId(id), "套餐配置已绑定购买事件,不能删除");
|
||||
super.delete(id);
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
||||
/// 套餐配置只允许逻辑删除
|
||||
///
|
||||
/// @param id 套餐配置 ID
|
||||
@Override
|
||||
public void destroy(String id) {
|
||||
TimiException.requiredTrue(false, "套餐配置不允许物理删除");
|
||||
}
|
||||
|
||||
private void prepareConfig(GaoPackageConfig config, String excludeId) {
|
||||
String name = trimRequired(config.getName(), "套餐名称不能为空");
|
||||
TimiException.requiredNull(mapper.selectByStoreIdAndName(config.getStoreId(), name, excludeId), "门店内套餐名称不能重复");
|
||||
config.setName(name);
|
||||
config.setStandardAmount(TimiJava.defaultIfNull(config.getStandardAmount(), 0L));
|
||||
TimiException.requiredTrue(0 <= config.getStandardAmount(), "套餐标准价不能为负数");
|
||||
config.setStatus(TimiJava.defaultIfNull(config.getStatus(), GaoPackageConfig.Status.DRAFT));
|
||||
config.setSort(TimiJava.defaultIfNull(config.getSort(), 0));
|
||||
TimiException.requiredTrue(config.getBeginAt() == null || config.getEndAt() == null || config.getBeginAt() <= config.getEndAt(), "套餐有效期无效");
|
||||
if (config.getItemList() != null) {
|
||||
verifyItems(config.getItemList());
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyItems(List<GaoPackageConfigItem> itemList) {
|
||||
TimiException.requiredTrue(!itemList.isEmpty(), "套餐至少配置一个产品");
|
||||
Set<String> keySet = new HashSet<>();
|
||||
for (int i = 0; i < itemList.size(); i++) {
|
||||
GaoPackageConfigItem item = itemList.get(i);
|
||||
TimiException.required(item, "套餐产品明细不能为空");
|
||||
String name = trimRequired(item.getProductName(), "产品名称不能为空");
|
||||
String unit = trimRequired(item.getUnit(), "产品单位不能为空");
|
||||
BigDecimal quantity = item.getDefaultQuantity();
|
||||
TimiException.required(quantity, "产品默认数量不能为空");
|
||||
TimiException.requiredTrue(0 < quantity.signum(), "产品默认数量必须大于 0");
|
||||
TimiException.requiredTrue(3 >= quantity.scale(), "产品数量最多保留三位小数");
|
||||
String key = name + "|" + unit;
|
||||
TimiException.requiredTrue(keySet.add(key), "同一套餐内产品名称和单位不能重复");
|
||||
item.setProductName(name);
|
||||
item.setUnit(unit);
|
||||
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveItems(String configId, List<GaoPackageConfigItem> itemList) {
|
||||
for (GaoPackageConfigItem source : TimiJava.safeIterable(itemList)) {
|
||||
GaoPackageConfigItem item = new GaoPackageConfigItem();
|
||||
item.setPackageConfigId(configId);
|
||||
item.setProductName(source.getProductName());
|
||||
item.setUnit(source.getUnit());
|
||||
item.setDefaultQuantity(source.getDefaultQuantity());
|
||||
item.setSort(source.getSort());
|
||||
item.setRemark(source.getRemark());
|
||||
itemMapper.insert(item);
|
||||
}
|
||||
}
|
||||
|
||||
private String trimRequired(String value, String message) {
|
||||
TimiException.required(value, message);
|
||||
String result = value.trim();
|
||||
TimiException.requiredTrue(!result.isEmpty(), message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
package com.imyeyu.api.modules.gao.service.implement;
|
||||
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDelivery;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageDeliveryItem;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageDeliveryItemMapper;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageDeliveryMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPackageDeliveryService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryActionRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryCreateRequest;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryPage;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoPackageDeliveryRevokeRequest;
|
||||
import com.imyeyu.api.modules.system.entity.Logger;
|
||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
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.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/// GAO 套餐发放服务实现
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GaoPackageDeliveryServiceImplement extends AbstractEntityService<GaoPackageDelivery, String> implements GaoPackageDeliveryService {
|
||||
|
||||
private static final BigDecimal ZERO = BigDecimal.ZERO;
|
||||
|
||||
private final GaoPackageDeliveryMapper mapper;
|
||||
private final GaoPackageDeliveryItemMapper itemMapper;
|
||||
private final GaoCustomerPackageService customerPackageService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final GaoStoreScopeService storeScopeService;
|
||||
private final RoleChecker roleChecker;
|
||||
private final UserLoginService userLoginService;
|
||||
private final LoggerService loggerService;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<GaoPackageDelivery, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
/// 发放流水必须通过专用操作创建
|
||||
///
|
||||
/// @param delivery 发放流水
|
||||
@Override
|
||||
public void create(GaoPackageDelivery delivery) {
|
||||
throw new TimiException(TimiCode.ERROR, "发放流水必须通过专用接口创建");
|
||||
}
|
||||
|
||||
/// 发放流水不允许通用修改
|
||||
///
|
||||
/// @param delivery 发放流水
|
||||
@Override
|
||||
public void update(GaoPackageDelivery delivery) {
|
||||
throw new TimiException(TimiCode.ERROR, "发放流水不允许通用修改");
|
||||
}
|
||||
|
||||
/// 发放流水不允许删除
|
||||
///
|
||||
/// @param id 发放流水 ID
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
throw new TimiException(TimiCode.ERROR, "发放流水不允许删除");
|
||||
}
|
||||
|
||||
/// 发放流水不允许物理删除
|
||||
///
|
||||
/// @param id 发放流水 ID
|
||||
@Override
|
||||
public void destroy(String id) {
|
||||
throw new TimiException(TimiCode.ERROR, "发放流水不允许物理删除");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoPackageDelivery> pageByQuery(GaoPackageDeliveryPage page) {
|
||||
TimiException.required(page, "not found page");
|
||||
PageResult<GaoPackageDelivery> result = new PageResult<>();
|
||||
result.setTotal(mapper.countByQuery(page));
|
||||
result.setList(mapper.selectByQuery(page));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GaoPackageDelivery> listByCustomerPackageId(String customerPackageId) {
|
||||
TimiException.required(customerPackageId, "not found customerPackageId");
|
||||
return mapper.selectByCustomerPackageId(customerPackageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<GaoPackageDeliveryItem>> mapItemListByDeliveryIdList(Collection<String> deliveryIdList) {
|
||||
if (deliveryIdList == null || deliveryIdList.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return itemMapper.selectByDeliveryIdList(deliveryIdList).stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(GaoPackageDeliveryItem::getDeliveryId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countPendingByCustomerPackageId(String customerPackageId) {
|
||||
return mapper.countPendingByCustomerPackageId(customerPackageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countVerifiedByCustomerPackageId(String customerPackageId) {
|
||||
return mapper.countVerifiedByCustomerPackageId(customerPackageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoPackageDelivery get(String id) {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoPackageDelivery createDelivery(GaoPackageDeliveryCreateRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_DELIVERY_CREATE", request, () -> createDeliveryInternal(request));
|
||||
}
|
||||
|
||||
private GaoPackageDelivery createDeliveryInternal(GaoPackageDeliveryCreateRequest request) {
|
||||
TimiException.required(request, "not found delivery request");
|
||||
TimiException.required(request.getRequestNo(), "not found requestNo");
|
||||
request.setRequestNo(request.getRequestNo().trim());
|
||||
TimiException.required(request.getCustomerPackageId(), "not found customerPackageId");
|
||||
GaoCustomerPackage packageEntity = customerPackageService.getForUpdate(request.getCustomerPackageId());
|
||||
TimiException.required(packageEntity, "not found customer package");
|
||||
storeScopeService.checkStoreId(packageEntity.getStoreId());
|
||||
GaoPackageDelivery existing = mapper.selectByRequestNo(packageEntity.getStoreId(), request.getRequestNo());
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(existing.getOperationType() == GaoPackageDelivery.OperationType.DELIVER, "requestNo 已被其他发放操作使用");
|
||||
verifyCreateIdempotency(existing, request);
|
||||
return get(existing.getId());
|
||||
}
|
||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "发放必须上传照片");
|
||||
TimiException.requiredTrue(packageEntity.getStatus() == GaoCustomerPackage.Status.ACTIVE, "当前套餐不可发放");
|
||||
checkPaymentForDelivery(packageEntity);
|
||||
List<GaoPackageDeliveryItem> requestItemList = validateRequestItems(request.getItemList());
|
||||
Map<String, GaoCustomerPackageItem> packageItemMap = new HashMap<>();
|
||||
for (GaoPackageDeliveryItem requestItem : requestItemList) {
|
||||
GaoCustomerPackageItem packageItem = customerPackageService.getItemForUpdate(requestItem.getCustomerPackageItemId());
|
||||
TimiException.required(packageItem, "not found customer package item");
|
||||
TimiException.requiredTrue(packageEntity.getId().equals(packageItem.getCustomerPackageId()), "发放产品不属于当前套餐");
|
||||
packageItemMap.put(packageItem.getId(), packageItem);
|
||||
}
|
||||
|
||||
GaoPackageDelivery delivery = new GaoPackageDelivery();
|
||||
delivery.setCustomerPackageId(packageEntity.getId());
|
||||
delivery.setStoreId(packageEntity.getStoreId());
|
||||
delivery.setCustomerId(packageEntity.getCustomerId());
|
||||
delivery.setOperationType(GaoPackageDelivery.OperationType.DELIVER);
|
||||
delivery.setStatus(GaoPackageDelivery.Status.PENDING);
|
||||
delivery.setRequestNo(request.getRequestNo());
|
||||
delivery.setOperatorUserId(userLoginService.getRequireLoginUserId());
|
||||
delivery.setDeliveredAt(TimiJava.defaultIfNull(request.getDeliveredAt(), Time.now()));
|
||||
delivery.setRemark(request.getRemark());
|
||||
super.create(delivery);
|
||||
for (GaoPackageDeliveryItem requestItem : requestItemList) {
|
||||
GaoCustomerPackageItem packageItem = packageItemMap.get(requestItem.getCustomerPackageItemId());
|
||||
BigDecimal reservedBefore = decimal(packageItem.getReservedQuantity());
|
||||
BigDecimal deliveredBefore = decimal(packageItem.getDeliveredQuantity());
|
||||
BigDecimal available = decimal(packageItem.getTotalQuantity()).subtract(reservedBefore).subtract(deliveredBefore);
|
||||
TimiException.requiredTrue(0 <= available.compareTo(requestItem.getQuantity()), "产品可发数量不足");
|
||||
int updated = customerPackageService.updateItemReservedQuantity(packageItem.getId(), requestItem.getQuantity());
|
||||
TimiException.requiredTrue(0 < updated, "产品可发数量不足");
|
||||
GaoPackageDeliveryItem savedItem = copyDeliveryItem(requestItem);
|
||||
savedItem.setDeliveryId(delivery.getId());
|
||||
savedItem.setReservedBefore(reservedBefore);
|
||||
savedItem.setReservedAfter(reservedBefore.add(requestItem.getQuantity()));
|
||||
savedItem.setDeliveredBefore(deliveredBefore);
|
||||
savedItem.setDeliveredAfter(deliveredBefore);
|
||||
savedItem.setProductNameSnapshot(packageItem.getProductNameSnapshot());
|
||||
savedItem.setUnitSnapshot(packageItem.getUnitSnapshot());
|
||||
itemMapper.insert(savedItem);
|
||||
}
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_PACKAGE_DELIVERY, delivery.getId(), request.getAttachmentList());
|
||||
return delivery;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoPackageDelivery verify(String deliveryId) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_DELIVERY_VERIFY", deliveryId, () -> verifyInternal(deliveryId));
|
||||
}
|
||||
|
||||
private GaoPackageDelivery verifyInternal(String deliveryId) {
|
||||
GaoPackageDelivery delivery = mapper.selectForUpdate(deliveryId);
|
||||
TimiException.required(delivery, "not found delivery");
|
||||
storeScopeService.checkStoreId(delivery.getStoreId());
|
||||
TimiException.requiredTrue(delivery.getStatus() == GaoPackageDelivery.Status.PENDING && delivery.getOperationType() == GaoPackageDelivery.OperationType.DELIVER, "发放单不是待核销状态");
|
||||
GaoCustomerPackage packageEntity = customerPackageService.getForUpdate(delivery.getCustomerPackageId());
|
||||
TimiException.required(packageEntity, "not found customer package");
|
||||
TimiException.requiredTrue(packageEntity.getStatus() == GaoCustomerPackage.Status.ACTIVE, "当前套餐不可核销发放");
|
||||
checkPaymentForDelivery(packageEntity);
|
||||
List<GaoPackageDeliveryItem> itemList = itemMapper.selectByDeliveryId(delivery.getId());
|
||||
for (GaoPackageDeliveryItem deliveryItem : itemList) {
|
||||
GaoCustomerPackageItem packageItem = customerPackageService.getItemForUpdate(deliveryItem.getCustomerPackageItemId());
|
||||
TimiException.required(packageItem, "not found customer package item");
|
||||
TimiException.requiredTrue(0 < customerPackageService.updateItemReservedQuantity(packageItem.getId(), deliveryItem.getQuantity().negate()), "发放占用数量异常");
|
||||
TimiException.requiredTrue(0 < customerPackageService.updateItemDeliveredQuantity(packageItem.getId(), deliveryItem.getQuantity()), "发放数量异常");
|
||||
}
|
||||
TimiException.requiredTrue(0 < mapper.updateStatusIf(delivery.getId(), GaoPackageDelivery.Status.PENDING, GaoPackageDelivery.Status.VERIFIED, userLoginService.getRequireLoginUserId(), Time.now(), delivery.getReason()), "发放单状态已变化");
|
||||
delivery.setStatus(GaoPackageDelivery.Status.VERIFIED);
|
||||
delivery.setVerifierUserId(userLoginService.getRequireLoginUserId());
|
||||
delivery.setVerifiedAt(Time.now());
|
||||
refreshPackageStatus(packageEntity);
|
||||
return delivery;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoPackageDelivery reject(GaoPackageDeliveryActionRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_DELIVERY_REJECT", request, () -> releasePending(request, GaoPackageDelivery.Status.REJECTED));
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoPackageDelivery cancel(GaoPackageDeliveryActionRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_DELIVERY_CANCEL", request, () -> cancelInternal(request));
|
||||
}
|
||||
|
||||
private GaoPackageDelivery cancelInternal(GaoPackageDeliveryActionRequest request) {
|
||||
TimiException.required(request, "not found delivery request");
|
||||
GaoPackageDelivery delivery = mapper.selectForUpdate(request.getDeliveryId());
|
||||
TimiException.required(delivery, "not found delivery");
|
||||
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||
TimiException.requiredTrue(loginUserId.equals(delivery.getOperatorUserId()), "只能撤销自己提交的发放");
|
||||
return releasePending(request, GaoPackageDelivery.Status.CANCELLED);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public GaoPackageDelivery revoke(GaoPackageDeliveryRevokeRequest request) {
|
||||
return loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_DELIVERY_REVOKE", request, () -> revokeInternal(request));
|
||||
}
|
||||
|
||||
private GaoPackageDelivery revokeInternal(GaoPackageDeliveryRevokeRequest request) {
|
||||
TimiException.required(request, "not found revoke request");
|
||||
TimiException.required(request.getRequestNo(), "not found requestNo");
|
||||
request.setRequestNo(request.getRequestNo().trim());
|
||||
TimiException.required(request.getDeliveryId(), "not found deliveryId");
|
||||
TimiException.required(hasReason(request.getReason()), "冲正原因不能为空");
|
||||
GaoPackageDelivery source = mapper.selectForUpdate(request.getDeliveryId());
|
||||
TimiException.required(source, "not found delivery");
|
||||
storeScopeService.checkStoreId(source.getStoreId());
|
||||
GaoPackageDelivery existing = mapper.selectByRequestNo(source.getStoreId(), request.getRequestNo());
|
||||
if (existing != null) {
|
||||
TimiException.requiredTrue(existing.getOperationType() == GaoPackageDelivery.OperationType.REVOKE
|
||||
&& source.getId().equals(existing.getSourceDeliveryId()), "requestNo 已被其他发放操作使用");
|
||||
return get(existing.getId());
|
||||
}
|
||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "发放冲正必须上传照片");
|
||||
TimiException.requiredTrue(source.getOperationType() == GaoPackageDelivery.OperationType.DELIVER && source.getStatus() == GaoPackageDelivery.Status.VERIFIED, "只有已核销发放可以冲正");
|
||||
TimiException.requiredNull(mapper.selectRevokeBySourceDeliveryId(source.getId()), "该发放单已经冲正");
|
||||
GaoCustomerPackage packageEntity = customerPackageService.getForUpdate(source.getCustomerPackageId());
|
||||
TimiException.required(packageEntity, "not found customer package");
|
||||
List<GaoPackageDeliveryItem> sourceItemList = itemMapper.selectByDeliveryId(source.getId());
|
||||
GaoPackageDelivery revoke = new GaoPackageDelivery();
|
||||
revoke.setCustomerPackageId(source.getCustomerPackageId());
|
||||
revoke.setStoreId(source.getStoreId());
|
||||
revoke.setCustomerId(source.getCustomerId());
|
||||
revoke.setOperationType(GaoPackageDelivery.OperationType.REVOKE);
|
||||
revoke.setStatus(GaoPackageDelivery.Status.VERIFIED);
|
||||
revoke.setRequestNo(request.getRequestNo());
|
||||
revoke.setOperatorUserId(userLoginService.getRequireLoginUserId());
|
||||
revoke.setDeliveredAt(TimiJava.defaultIfNull(request.getDeliveredAt(), Time.now()));
|
||||
revoke.setVerifierUserId(userLoginService.getRequireLoginUserId());
|
||||
revoke.setVerifiedAt(Time.now());
|
||||
revoke.setSourceDeliveryId(source.getId());
|
||||
revoke.setReason(request.getReason().trim());
|
||||
revoke.setRemark(request.getRemark());
|
||||
super.create(revoke);
|
||||
for (GaoPackageDeliveryItem sourceItem : sourceItemList) {
|
||||
GaoCustomerPackageItem packageItem = customerPackageService.getItemForUpdate(sourceItem.getCustomerPackageItemId());
|
||||
TimiException.required(packageItem, "not found customer package item");
|
||||
BigDecimal deliveredBefore = decimal(packageItem.getDeliveredQuantity());
|
||||
TimiException.requiredTrue(0 < deliveredBefore.compareTo(sourceItem.getQuantity()) || deliveredBefore.compareTo(sourceItem.getQuantity()) == 0, "已发数量不足以冲正");
|
||||
TimiException.requiredTrue(0 < customerPackageService.updateItemDeliveredQuantity(packageItem.getId(), sourceItem.getQuantity().negate()), "发放数量冲正失败");
|
||||
GaoPackageDeliveryItem revokeItem = copyDeliveryItem(sourceItem);
|
||||
revokeItem.setDeliveryId(revoke.getId());
|
||||
revokeItem.setReservedBefore(packageItem.getReservedQuantity());
|
||||
revokeItem.setReservedAfter(packageItem.getReservedQuantity());
|
||||
revokeItem.setDeliveredBefore(deliveredBefore);
|
||||
revokeItem.setDeliveredAfter(deliveredBefore.subtract(sourceItem.getQuantity()));
|
||||
itemMapper.insert(revokeItem);
|
||||
}
|
||||
TimiException.requiredTrue(0 < mapper.updateStatusIf(source.getId(), GaoPackageDelivery.Status.VERIFIED, GaoPackageDelivery.Status.REVOKED, source.getVerifierUserId(), source.getVerifiedAt(), source.getReason()), "发放单状态已变化");
|
||||
if (packageEntity.getStatus() == GaoCustomerPackage.Status.FULFILLED) {
|
||||
packageEntity.setStatus(GaoCustomerPackage.Status.ACTIVE);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
}
|
||||
attachmentService.updateByBizId(Attachment.BizType.GAO_PACKAGE_DELIVERY, revoke.getId(), request.getAttachmentList());
|
||||
return revoke;
|
||||
}
|
||||
|
||||
private GaoPackageDelivery releasePending(GaoPackageDeliveryActionRequest request, GaoPackageDelivery.Status targetStatus) {
|
||||
TimiException.required(request, "not found delivery request");
|
||||
TimiException.required(request.getDeliveryId(), "not found deliveryId");
|
||||
TimiException.required(hasReason(request.getReason()), "拒绝或撤销原因不能为空");
|
||||
GaoPackageDelivery delivery = mapper.selectForUpdate(request.getDeliveryId());
|
||||
TimiException.required(delivery, "not found delivery");
|
||||
storeScopeService.checkStoreId(delivery.getStoreId());
|
||||
TimiException.requiredTrue(delivery.getStatus() == GaoPackageDelivery.Status.PENDING && delivery.getOperationType() == GaoPackageDelivery.OperationType.DELIVER, "发放单不是待核销状态");
|
||||
for (GaoPackageDeliveryItem item : itemMapper.selectByDeliveryId(delivery.getId())) {
|
||||
TimiException.requiredTrue(0 < customerPackageService.updateItemReservedQuantity(item.getCustomerPackageItemId(), item.getQuantity().negate()), "发放占用数量异常");
|
||||
}
|
||||
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||
TimiException.requiredTrue(0 < mapper.updateStatusIf(delivery.getId(), GaoPackageDelivery.Status.PENDING, targetStatus, loginUserId, Time.now(), request.getReason().trim()), "发放单状态已变化");
|
||||
delivery.setStatus(targetStatus);
|
||||
delivery.setVerifierUserId(loginUserId);
|
||||
delivery.setVerifiedAt(Time.now());
|
||||
delivery.setReason(request.getReason().trim());
|
||||
return delivery;
|
||||
}
|
||||
|
||||
private void refreshPackageStatus(GaoCustomerPackage packageEntity) {
|
||||
List<GaoCustomerPackageItem> itemList = customerPackageService.listItemByCustomerPackageId(packageEntity.getId());
|
||||
boolean fulfilled = !itemList.isEmpty();
|
||||
for (GaoCustomerPackageItem item : itemList) {
|
||||
if (decimal(item.getDeliveredQuantity()).compareTo(decimal(item.getTotalQuantity())) < 0) {
|
||||
fulfilled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fulfilled && packageEntity.getStatus() == GaoCustomerPackage.Status.ACTIVE) {
|
||||
packageEntity.setStatus(GaoCustomerPackage.Status.FULFILLED);
|
||||
customerPackageService.updateAggregateState(packageEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private List<GaoPackageDeliveryItem> validateRequestItems(List<GaoPackageDeliveryItem> sourceList) {
|
||||
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "发放至少需要一个产品");
|
||||
Set<String> idSet = new HashSet<>();
|
||||
List<GaoPackageDeliveryItem> result = new ArrayList<>();
|
||||
for (GaoPackageDeliveryItem source : sourceList) {
|
||||
TimiException.required(source, "发放产品明细不能为空");
|
||||
TimiException.required(source.getCustomerPackageItemId(), "not found customerPackageItemId");
|
||||
TimiException.required(source.getQuantity(), "发放数量不能为空");
|
||||
TimiException.requiredTrue(0 < source.getQuantity().signum(), "发放数量必须大于 0");
|
||||
TimiException.requiredTrue(3 >= source.getQuantity().scale(), "发放数量最多保留三位小数");
|
||||
TimiException.requiredTrue(idSet.add(source.getCustomerPackageItemId()), "同一发放单产品不能重复");
|
||||
result.add(source);
|
||||
}
|
||||
result.sort(Comparator.comparing(GaoPackageDeliveryItem::getCustomerPackageItemId));
|
||||
return result;
|
||||
}
|
||||
|
||||
private GaoPackageDeliveryItem copyDeliveryItem(GaoPackageDeliveryItem source) {
|
||||
GaoPackageDeliveryItem result = new GaoPackageDeliveryItem();
|
||||
result.setCustomerPackageItemId(source.getCustomerPackageItemId());
|
||||
result.setQuantity(source.getQuantity());
|
||||
return result;
|
||||
}
|
||||
|
||||
private void verifyCreateIdempotency(GaoPackageDelivery existing, GaoPackageDeliveryCreateRequest request) {
|
||||
TimiException.requiredTrue(existing.getCustomerPackageId().equals(request.getCustomerPackageId())
|
||||
&& (request.getDeliveredAt() == null || request.getDeliveredAt().equals(existing.getDeliveredAt()))
|
||||
&& sameText(existing.getRemark(), request.getRemark()), "requestNo 对应请求内容不一致");
|
||||
List<GaoPackageDeliveryItem> existingItems = itemMapper.selectByDeliveryId(existing.getId());
|
||||
List<GaoPackageDeliveryItem> requestItems = validateRequestItems(request.getItemList());
|
||||
TimiException.requiredTrue(existingItems.size() == requestItems.size(), "requestNo 对应请求内容不一致");
|
||||
Map<String, BigDecimal> requestMap = requestItems.stream().collect(java.util.stream.Collectors.toMap(GaoPackageDeliveryItem::getCustomerPackageItemId, GaoPackageDeliveryItem::getQuantity));
|
||||
for (GaoPackageDeliveryItem item : existingItems) {
|
||||
TimiException.requiredTrue(item.getQuantity().compareTo(requestMap.getOrDefault(item.getCustomerPackageItemId(), ZERO)) == 0, "requestNo 对应请求内容不一致");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAttachment(List<Attachment> attachmentList) {
|
||||
for (Attachment attachment : TimiJava.safeIterable(attachmentList)) {
|
||||
if (attachment != null && (attachment.getAttachType() == null || "PHOTO".equalsIgnoreCase(attachment.getAttachType()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasReason(String reason) {
|
||||
return reason != null && !reason.trim().isEmpty();
|
||||
}
|
||||
|
||||
private boolean canDeliver(GaoCustomerPackage packageEntity) {
|
||||
return safeLong(packageEntity.getSaleAmount()) == 0 || 0 < safeLong(packageEntity.getPaidAmount());
|
||||
}
|
||||
|
||||
private void checkPaymentForDelivery(GaoCustomerPackage packageEntity) {
|
||||
long saleAmount = safeLong(packageEntity.getSaleAmount());
|
||||
long paidAmount = safeLong(packageEntity.getPaidAmount());
|
||||
TimiException.requiredTrue(canDeliver(packageEntity), "套餐未有已核销收款,不能发放");
|
||||
if (0 < paidAmount && paidAmount < saleAmount) {
|
||||
roleChecker.checkAny(ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sameText(String left, String right) {
|
||||
return left == null ? right == null : left.equals(right);
|
||||
}
|
||||
|
||||
private BigDecimal decimal(BigDecimal value) {
|
||||
return value == null ? ZERO : value;
|
||||
}
|
||||
|
||||
private long safeLong(Long value) {
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package com.imyeyu.api.modules.gao.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.Article;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.mapper.ArticleMapper;
|
||||
import com.imyeyu.api.modules.common.service.ArticleService;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.gao.mapper.GaoStoreDiaryMapper;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreDiaryService;
|
||||
import com.imyeyu.api.modules.gao.vo.GaoStoreDiaryPage;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/// GAO 门店日记服务实现
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GaoStoreDiaryServiceImplement implements GaoStoreDiaryService {
|
||||
|
||||
private final ArticleService articleService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final ArticleMapper articleMapper;
|
||||
private final GaoStoreDiaryMapper diaryMapper;
|
||||
|
||||
@Override
|
||||
public PageResult<Article> page(GaoStoreDiaryPage page) {
|
||||
PageResult<Article> result = new PageResult<>();
|
||||
result.setTotal(diaryMapper.countByMonth(page));
|
||||
result.setList(diaryMapper.selectByMonth(page));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Article get(String id) {
|
||||
Article article = articleService.get(id);
|
||||
checkStoreDiary(article);
|
||||
return article;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
public void create(Article article, String userId) {
|
||||
prepare(article, userId);
|
||||
articleService.create(article);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
public void update(Article article, String userId) {
|
||||
TimiException.required(article, "未找到门店日记");
|
||||
TimiException.required(article.getId(), "未找到门店日记 ID");
|
||||
Article dbArticle = get(article.getId());
|
||||
article.setBizType(dbArticle.getBizType());
|
||||
article.setBizId(dbArticle.getBizId());
|
||||
article.setCreatedAt(null);
|
||||
article.setUpdatedAt(null);
|
||||
article.setDeletedAt(null);
|
||||
article.setReads(dbArticle.getReads());
|
||||
article.setLikes(dbArticle.getLikes());
|
||||
article.setUpdatedBy(userId);
|
||||
articleService.update(article);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
public void delete(String id) {
|
||||
Article article = get(id);
|
||||
attachmentService.deleteByBizId(Attachment.BizType.ARTICLE, article.getId());
|
||||
articleService.delete(article.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
public void deleteByStoreId(String storeId) {
|
||||
TimiException.required(storeId, "未找到门店 ID");
|
||||
Article example = new Article();
|
||||
example.setBizType(Article.BizType.GAO_STORE_DIARY);
|
||||
example.setBizId(storeId);
|
||||
for (Article article : articleMapper.selectAllByExample(example)) {
|
||||
attachmentService.deleteByBizId(Attachment.BizType.ARTICLE, article.getId());
|
||||
articleService.delete(article.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/// 固定门店日记业务类型并写入更新用户
|
||||
///
|
||||
/// @param article 门店日记
|
||||
/// @param userId 操作用户 ID
|
||||
private void prepare(Article article, String userId) {
|
||||
TimiException.required(article, "未找到门店日记");
|
||||
TimiException.required(article.getBizId(), "未找到门店 ID");
|
||||
TimiException.required(userId, "未找到更新用户 ID");
|
||||
article.setBizType(Article.BizType.GAO_STORE_DIARY);
|
||||
article.setCreatedAt(null);
|
||||
article.setUpdatedAt(null);
|
||||
article.setDeletedAt(null);
|
||||
article.setUpdatedBy(userId);
|
||||
article.setReads(0);
|
||||
article.setLikes(0);
|
||||
article.setStatus(TimiJava.defaultIfNull(article.getStatus(), Article.Status.NORMAL));
|
||||
}
|
||||
|
||||
/// 校验文章确实是门店日记
|
||||
///
|
||||
/// @param article 文章
|
||||
private void checkStoreDiary(Article article) {
|
||||
TimiException.required(article, "未找到门店日记");
|
||||
TimiException.requiredTrue(article.getBizType() == Article.BizType.GAO_STORE_DIARY, "门店日记类型无效");
|
||||
TimiException.required(article.getBizId(), "未找到门店 ID");
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.imyeyu.api.modules.gao.service.implement;
|
||||
|
||||
import com.imyeyu.api.bean.CoreRoleCode;
|
||||
import com.imyeyu.api.bean.ModuleCode;
|
||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||
import com.imyeyu.api.modules.gao.entity.GaoStore;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// GAO 门店资源范围服务实现
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-23
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GaoStoreScopeServiceImplement implements GaoStoreScopeService {
|
||||
|
||||
private final RoleChecker roleChecker;
|
||||
private final GaoStoreService gaoStoreService;
|
||||
|
||||
@Override
|
||||
public boolean hasGlobalScope() {
|
||||
return roleChecker.hasAny(ModuleCode.CORE, CoreRoleCode.SYSTEM.name(), CoreRoleCode.ADMIN.name())
|
||||
|| roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveStoreId(String requestedStoreId) {
|
||||
if (hasGlobalScope()) {
|
||||
return requestedStoreId;
|
||||
}
|
||||
String currentStoreId = gaoStoreService.getBelongIdByRequiredLoginUserId();
|
||||
if (TimiJava.isNotEmpty(requestedStoreId)) {
|
||||
TimiException.requiredTrue(currentStoreId.equals(requestedStoreId), "无权操作其他门店");
|
||||
}
|
||||
return currentStoreId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkStoreId(String storeId) {
|
||||
TimiException.required(storeId, "not found storeId");
|
||||
if (!hasGlobalScope()) {
|
||||
String currentStoreId = gaoStoreService.getBelongIdByRequiredLoginUserId();
|
||||
TimiException.requiredTrue(currentStoreId.equals(storeId), "无权操作其他门店");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkStore(GaoStore store) {
|
||||
TimiException.required(store, "not found store");
|
||||
checkStoreId(store.getId());
|
||||
}
|
||||
}
|
||||
+10
@@ -11,6 +11,7 @@ import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoPointRuleService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreBusinessDayService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreDiaryService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||
import com.imyeyu.api.modules.gao.service.GaoUserService;
|
||||
import com.imyeyu.api.modules.gao.util.StoreXSSFBuilder;
|
||||
@@ -55,6 +56,7 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
|
||||
private final GaoPointAccountService pointAccountService;
|
||||
private final GaoPointRuleService pointRuleService;
|
||||
private final GaoStoreBusinessDayService businessDayService;
|
||||
private final GaoStoreDiaryService storeDiaryService;
|
||||
private final ObjectProvider<GaoUserService> gaoUserServiceProvider;
|
||||
private final TagService tagService;
|
||||
|
||||
@@ -140,6 +142,7 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
|
||||
deleteGaoUsers(store.getId());
|
||||
deletePointAccounts(store.getId());
|
||||
deleteStoreTags(store.getId());
|
||||
deleteStoreDiaries(store.getId());
|
||||
store.setStatus(GaoStore.Status.DELETED);
|
||||
super.update(store);
|
||||
super.delete(id);
|
||||
@@ -208,6 +211,13 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
|
||||
tagService.deleteByOwner(Tag.OwnerType.GAO_STORE, storeId);
|
||||
}
|
||||
|
||||
/// 删除门店日记及其附件
|
||||
///
|
||||
/// @param storeId 门店 ID
|
||||
private void deleteStoreDiaries(String storeId) {
|
||||
storeDiaryService.deleteByStoreId(storeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaoStore getByUserId(String userId) {
|
||||
TimiException.required(userId, "not found userId");
|
||||
|
||||
+24
@@ -13,12 +13,19 @@ import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
///
|
||||
/// GAO 用户服务实现
|
||||
///
|
||||
@@ -160,4 +167,21 @@ public class GaoUserServiceImplement extends AbstractEntityService<GaoUser, Stri
|
||||
loggerService.create(logger);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, GaoUser> mapByUserIdList(Collection<String> userIdList) {
|
||||
if (TimiJava.isEmpty(userIdList)) {
|
||||
return Map.of();
|
||||
}
|
||||
return mapper.selectByUserIdList(userIdList).stream()
|
||||
.collect(Collectors.toMap(GaoUser::getUserId, Function.identity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<GaoUser> pageRecipients(Page<GaoUser> page) {
|
||||
PageResult<GaoUser> result = new PageResult<>();
|
||||
result.setTotal(mapper.countRecipients(page));
|
||||
result.setList(mapper.selectRecipientPage(page));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/// GAO 取消客户套餐请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoCustomerPackageCancelRequest {
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 取消原因
|
||||
private String reason;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||
import com.imyeyu.java.bean.BasePage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/// GAO 客户套餐分页查询请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoCustomerPackagePage extends BasePage {
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 客户或套餐关键词
|
||||
private String keyword;
|
||||
|
||||
/// 套餐状态
|
||||
private GaoCustomerPackage.Status status;
|
||||
|
||||
/// 付款状态
|
||||
private GaoCustomerPackage.PaymentStatus paymentStatus;
|
||||
|
||||
/// 退款状态
|
||||
private GaoCustomerPackage.RefundStatus refundStatus;
|
||||
|
||||
/// 购买开始时间
|
||||
private Long beginAt;
|
||||
|
||||
/// 购买结束时间
|
||||
private Long endAt;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/// GAO 客户套餐购买产品明细
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoCustomerPackagePurchaseItem {
|
||||
|
||||
/// 产品名称
|
||||
private String productName;
|
||||
|
||||
/// 产品单位
|
||||
private String unit;
|
||||
|
||||
/// 总权益数量
|
||||
private BigDecimal totalQuantity;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 客户购买套餐请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoCustomerPackagePurchaseRequest {
|
||||
|
||||
/// 客户端幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 购买事件 ID
|
||||
private String eventId;
|
||||
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 实际成交价,单位分,空为事件绑定套餐的标准价
|
||||
private Long saleAmount;
|
||||
|
||||
/// 购买时间
|
||||
private Long purchasedAt;
|
||||
|
||||
/// 最终产品明细,空为事件绑定套餐的默认产品明细
|
||||
private List<GaoCustomerPackagePurchaseItem> itemList;
|
||||
|
||||
/// 购买照片
|
||||
private List<Attachment> attachmentList;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/// 登记事件选项统计视图
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-30
|
||||
@Data
|
||||
public class GaoEventOptionStatView {
|
||||
|
||||
/// 选项组 ID
|
||||
private String groupId;
|
||||
|
||||
/// 选项组名称
|
||||
private String groupName;
|
||||
|
||||
/// 选项 ID
|
||||
private String optionId;
|
||||
|
||||
/// 选项名称
|
||||
private String optionLabel;
|
||||
|
||||
/// 登记记录数量
|
||||
private Long recordCount;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||
import com.imyeyu.java.bean.BasePage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -24,6 +25,12 @@ public class GaoEventRecordPage extends BasePage {
|
||||
/// 登记事件 ID 列表,为空查询全部登记事件
|
||||
private List<String> eventIdList;
|
||||
|
||||
/// 必须同时包含的选项 ID 列表
|
||||
private List<String> optionIdList;
|
||||
|
||||
/// 核销状态,为空查询全部状态
|
||||
private GaoEventRecord.VerificationStatus verificationStatus;
|
||||
|
||||
/// 关键词,匹配客户、登记事件、备注、登记人
|
||||
private String keyword;
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/// GAO 资金收款状态操作请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoFundActionRequest {
|
||||
|
||||
/// 资金记录 ID
|
||||
private String fundRecordId;
|
||||
|
||||
/// 拒绝或撤销原因
|
||||
private String reason;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/// GAO 确认无需退款请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoFundConfirmNoRefundRequest {
|
||||
|
||||
/// 客户端幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 确认时间
|
||||
private Long occurredAt;
|
||||
|
||||
/// 确认原因
|
||||
private String reason;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 线下收款登记请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoFundReceiveRequest {
|
||||
|
||||
/// 客户端幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 收款金额,单位分
|
||||
private Long amount;
|
||||
|
||||
/// 收款发生时间
|
||||
private Long occurredAt;
|
||||
|
||||
/// 收款照片
|
||||
private List<Attachment> attachmentList;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||
import com.imyeyu.java.bean.BasePage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/// GAO 资金流水分页查询请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoFundRecordPage extends BasePage {
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 客户 ID
|
||||
private String customerId;
|
||||
|
||||
/// 操作类型
|
||||
private GaoFundRecord.OperationType operationType;
|
||||
|
||||
/// 操作状态
|
||||
private GaoFundRecord.Status status;
|
||||
|
||||
/// 操作开始时间
|
||||
private Long beginAt;
|
||||
|
||||
/// 操作结束时间
|
||||
private Long endAt;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 退款登记请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoFundRefundRequest {
|
||||
|
||||
/// 客户端幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 客户套餐 ID
|
||||
private String customerPackageId;
|
||||
|
||||
/// 退款金额,单位分
|
||||
private Long amount;
|
||||
|
||||
/// 退款发生时间
|
||||
private Long occurredAt;
|
||||
|
||||
/// 退款原因
|
||||
private String reason;
|
||||
|
||||
/// 退款照片
|
||||
private List<Attachment> attachmentList;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// GAO 资金流水冲正请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
public class GaoFundRevokeRequest {
|
||||
|
||||
/// 客户端幂等请求号
|
||||
private String requestNo;
|
||||
|
||||
/// 原资金记录 ID
|
||||
private String fundRecordId;
|
||||
|
||||
/// 冲正发生时间
|
||||
private Long occurredAt;
|
||||
|
||||
/// 冲正原因
|
||||
private String reason;
|
||||
|
||||
/// 冲正照片
|
||||
private List<Attachment> attachmentList;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||
import com.imyeyu.java.bean.BasePage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/// GAO 套餐配置分页查询请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-31
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoPackageConfigPage extends BasePage {
|
||||
|
||||
/// 门店 ID
|
||||
private String storeId;
|
||||
|
||||
/// 套餐名称关键词
|
||||
private String keyword;
|
||||
|
||||
/// 套餐状态
|
||||
private GaoPackageConfig.Status status;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user