Compare commits

..
8 Commits
Author SHA1 Message Date
timi cd3d522d4f Merge pull request 'v1.0.24' (#34) from dev into master
Reviewed-on: #34
2026-08-22 09:56:13 +00:00
Timi 8de739cbb7 v1.0.24
CI / build-deploy (pull_request) Successful in 24s
CI / notify-on-failure (pull_request) Skipped
2026-08-22 17:55:54 +08:00
timi 181c5078e9 Merge pull request 'v1.0.23' (#33) from dev into master
Reviewed-on: #33
2026-08-22 09:51:37 +00:00
Timi e2e0023d13 v1.0.23
CI / build-deploy (pull_request) Successful in 23s
CI / notify-on-failure (pull_request) Skipped
2026-08-22 17:50:56 +08:00
Timi 8c6eed2379 update store delete 2026-08-22 17:21:30 +08:00
Timi c32ecd1f8c add point account list api 2026-08-22 16:42:09 +08:00
Timi 3c56e21efa add point totalConsumed 2026-08-22 11:45:00 +08:00
Timi 376f0ecd05 fix multi role invite error 2026-08-22 11:20:09 +08:00
24 changed files with 409 additions and 16 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
<groupId>com.imyeyu.timiserverapi</groupId>
<artifactId>TimiServerAPI</artifactId>
<version>1.0.22</version>
<version>1.0.24</version>
<packaging>jar</packaging>
<name>TimiServerAPI</name>
<description>imyeyu.com API</description>
@@ -39,4 +39,10 @@ public interface TagService extends BaseService<Tag, String> {
/// @param bizId 业务 ID
/// @param tagIdList 标签 ID 列表
void apply(TagApply.BizType bizType, String bizId, List<String> tagIdList);
/// 删除指定归属下的全部标签
///
/// @param ownerType 归属类型
/// @param ownerId 归属 ID
void deleteByOwner(Tag.OwnerType ownerType, String ownerId);
}
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.common.service.implement;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.common.entity.Tag;
import com.imyeyu.api.modules.common.entity.TagApply;
import com.imyeyu.api.modules.common.mapper.TagApplyMapper;
@@ -14,6 +15,7 @@ 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.HashSet;
@@ -104,6 +106,19 @@ public class TagServiceImplement extends AbstractEntityService<Tag, String> impl
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByOwner(Tag.OwnerType ownerType, String ownerId) {
TimiException.required(ownerType, "not found ownerType");
TimiException.required(ownerId, "not found ownerId");
Tag example = new Tag();
example.setOwnerType(ownerType);
example.setOwnerId(ownerId);
for (Tag tag : mapper.selectAllByExample(example)) {
super.delete(tag.getId());
}
}
private List<TagApply> listApply(TagApply.BizType bizType, String bizId) {
TimiException.required(bizType, "not found bizType");
TimiException.required(bizId, "not found bizId");
@@ -7,11 +7,15 @@ 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.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;
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;
@@ -32,12 +36,30 @@ public class GaoPointAccountController {
@AOPLog
@RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.POINT_ACCOUNT_READ)
@PostMapping("/account/detail")
@PostMapping("/account/list")
public PageResult<GaoPointAccount> list(@RequestBody GaoPointAccountPage page) {
TimiException.required(page, "not found page");
page.setStoreId(resolveStoreId(page.getStoreId()));
return accountService.pageByQuery(page);
}
@AOPLog
@RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.POINT_ACCOUNT_READ)
@PostMapping("/account/detail/customer")
public GaoPointAccount detail(@RequestParam String customerId, @RequestParam(required = false) String storeId) {
storeId = resolveStoreId(storeId);
return accountService.getByCustomerId(storeId, customerId);
}
@AOPLog
@RequestRateLimit
@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;
@@ -1,5 +1,6 @@
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;
@@ -24,6 +25,9 @@ public class GaoPointAccount extends UUIDEntity {
/// 累计获得积分
private Long totalEarned;
/// 累计消耗积分
private Long totalConsumed;
/// 累计撤销积分
private Long totalRevoked;
@@ -32,4 +36,8 @@ public class GaoPointAccount extends UUIDEntity {
/// 乐观锁版本
private Long version;
/// 客户信息
@Transient
private GaoCustomer customer;
}
@@ -1,9 +1,12 @@
package com.imyeyu.api.modules.gao.mapper;
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
import com.imyeyu.api.modules.gao.vo.GaoPointAccountPage;
import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/// GAO 客户积分账户 Mapper
///
/// @author Codex
@@ -17,6 +20,25 @@ public interface GaoPointAccountMapper extends BaseMapper<GaoPointAccount, Strin
/// @return 积分账户
GaoPointAccount selectByStoreIdAndCustomerId(@Param("storeId") String storeId, @Param("customerId") String customerId);
/// 按账户 ID 和门店查询积分账户
///
/// @param storeId 门店 ID,可为空
/// @param id 账户 ID
/// @return 积分账户
GaoPointAccount selectByIdAndStoreId(@Param("storeId") String storeId, @Param("id") String id);
/// 统计积分账户数量
///
/// @param page 查询条件
/// @return 账户数量
long countByQuery(GaoPointAccountPage page);
/// 分页查询积分账户
///
/// @param page 查询条件
/// @return 账户列表
List<GaoPointAccount> selectByQuery(GaoPointAccountPage page);
/// 创建客户积分账户,已存在时保持原账户不变
///
/// @param account 积分账户
@@ -33,9 +55,10 @@ public interface GaoPointAccountMapper extends BaseMapper<GaoPointAccount, Strin
/// @param id 账户 ID
/// @param delta 本次余额变化
/// @param totalEarnedDelta 累计获得积分变化
/// @param totalConsumedDelta 累计消耗积分变化
/// @param totalRevokedDelta 累计撤销积分变化
/// @param totalAdjustedDelta 累计人工调整积分变化
/// @param updatedAt 更新时间
/// @return 更新行数
int updateBalance(@Param("id") String id, @Param("delta") long delta, @Param("totalEarnedDelta") long totalEarnedDelta, @Param("totalRevokedDelta") long totalRevokedDelta, @Param("totalAdjustedDelta") long totalAdjustedDelta, @Param("updatedAt") long updatedAt);
int updateBalance(@Param("id") String id, @Param("delta") long delta, @Param("totalEarnedDelta") long totalEarnedDelta, @Param("totalConsumedDelta") long totalConsumedDelta, @Param("totalRevokedDelta") long totalRevokedDelta, @Param("totalAdjustedDelta") long totalAdjustedDelta, @Param("updatedAt") long updatedAt);
}
@@ -45,6 +45,11 @@ public interface GaoCustomerService extends BaseService<GaoCustomer, String> {
List<GaoCustomer> listByIdList(Collection<String> idList);
/// 删除门店下的全部客户及其关联业务数据
///
/// @param storeId 门店 ID
void deleteByStoreId(String storeId);
default Map<String, GaoCustomer> mapByIdList(Collection<String> idList) {
return listByIdList(idList).stream().collect(Collectors.toMap(GaoCustomer::getId, item -> item));
}
@@ -24,6 +24,11 @@ public interface GaoEventService extends BaseService<GaoEvent, String> {
/// @return 登记事件列表
List<GaoEvent> listValid(String storeId);
/// 删除门店下的全部登记事件及角色关联
///
/// @param storeId 门店 ID
void deleteByStoreId(String storeId);
/// 查询登记事件要求的角色
///
/// @param eventId 登记事件 ID
@@ -1,6 +1,8 @@
package com.imyeyu.api.modules.gao.service;
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
import com.imyeyu.api.modules.gao.vo.GaoPointAccountPage;
import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.spring.service.BaseService;
/// GAO 客户积分账户服务
@@ -9,6 +11,12 @@ import com.imyeyu.spring.service.BaseService;
/// @since 2026-08-11
public interface GaoPointAccountService extends BaseService<GaoPointAccount, String> {
/// 分页查询积分账户
///
/// @param page 查询条件
/// @return 账户分页
PageResult<GaoPointAccount> pageByQuery(GaoPointAccountPage page);
/// 查询客户当前有效积分账户
///
/// @param storeId 门店 ID
@@ -16,6 +24,18 @@ public interface GaoPointAccountService extends BaseService<GaoPointAccount, Str
/// @return 积分账户,不存在时返回 null
GaoPointAccount getByCustomerId(String storeId, String customerId);
/// 按账户 ID 查询积分账户
///
/// @param storeId 门店 ID,可为空
/// @param id 账户 ID
/// @return 积分账户,不存在时返回 null
GaoPointAccount getByIdAndStoreId(String storeId, String id);
/// 删除门店下的全部积分账户
///
/// @param storeId 门店 ID
void deleteByStoreId(String storeId);
/// 获取并锁定客户积分账户,不存在时创建
///
/// @param storeId 门店 ID
@@ -28,8 +48,9 @@ public interface GaoPointAccountService extends BaseService<GaoPointAccount, Str
/// @param accountId 账户 ID
/// @param delta 本次余额变化
/// @param totalEarnedDelta 累计获得积分变化
/// @param totalConsumedDelta 累计消耗积分变化
/// @param totalRevokedDelta 累计撤销积分变化
/// @param totalAdjustedDelta 累计人工调整积分变化
/// @return 更新行数
int updateBalance(String accountId, long delta, long totalEarnedDelta, long totalRevokedDelta, long totalAdjustedDelta);
int updateBalance(String accountId, long delta, long totalEarnedDelta, long totalConsumedDelta, long totalRevokedDelta, long totalAdjustedDelta);
}
@@ -25,6 +25,11 @@ public interface GaoPointRuleService extends BaseService<GaoPointRule, String> {
/// @return 规则列表
List<GaoPointRule> listByIdList(List<String> idList);
/// 删除门店下的全部积分规则
///
/// @param storeId 门店 ID
void deleteByStoreId(String storeId);
/// 按客户端提交的顺序更新启用规则优先级
///
/// @param idList 规则 ID 列表
@@ -36,4 +36,9 @@ public interface GaoStoreBusinessDayService extends BaseService<GaoStoreBusiness
/// @param endDateValue 结束日期值,格式为 yyyyMMdd
/// @return 营业日列表
java.util.List<GaoStoreBusinessDay> listByDateRange(String storeId, Integer beginDateValue, Integer endDateValue);
/// 删除门店下的全部营业日
///
/// @param storeId 门店 ID
void deleteByStoreId(String storeId);
}
@@ -10,6 +10,11 @@ import com.imyeyu.spring.service.BaseService;
/// @since 2026-07-30
public interface GaoUserService extends BaseService<GaoUser, String> {
/// 删除门店下的全部 GAO 用户关系,不删除核心用户
///
/// @param storeId 门店 ID
void deleteByStoreId(String storeId);
/// 调整 GAO 用户所属门店
///
/// @param gaoUser GAO 用户,必须包含 ID 和目标门店 ID
@@ -252,6 +252,17 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByStoreId(String storeId) {
TimiException.required(storeId, "not found storeId");
GaoCustomer example = new GaoCustomer();
example.setStoreId(storeId);
for (GaoCustomer customer : mapper.selectAllByExample(example)) {
delete(customer.getId());
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void updateWithAttachment(GaoCustomer customer) {
@@ -172,6 +172,20 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByStoreId(String storeId) {
TimiException.required(storeId, "not found storeId");
GaoEvent example = new GaoEvent();
example.setStoreId(storeId);
for (GaoEvent event : mapper.selectAllByExample(example)) {
GaoEventRoleRelation relationExample = new GaoEventRoleRelation();
relationExample.setEventId(event.getId());
roleRelationMapper.deleteAllByExample(relationExample);
delete(event.getId());
}
}
@Override
public Map<String, GaoEvent> mapByIdList(Collection<String> idList) {
return mapper.selectByIdList(new HashSet<>(idList)).stream().collect(Collectors.toMap(GaoEvent::getId, Function.identity()));
@@ -1,10 +1,16 @@
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.GaoCustomer;
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
import com.imyeyu.api.modules.gao.mapper.GaoCustomerMapper;
import com.imyeyu.api.modules.gao.mapper.GaoPointAccountMapper;
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
import com.imyeyu.api.modules.gao.vo.GaoPointAccountPage;
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;
@@ -12,7 +18,10 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
/// GAO 客户积分账户服务实现
///
@@ -23,12 +32,24 @@ import java.util.UUID;
public class GaoPointAccountServiceImplement extends AbstractEntityService<GaoPointAccount, String> implements GaoPointAccountService {
private final GaoPointAccountMapper mapper;
private final GaoCustomerMapper customerMapper;
private final AttachmentService attachmentService;
@Override
protected BaseMapper<GaoPointAccount, String> mapper() {
return mapper;
}
@Override
public PageResult<GaoPointAccount> pageByQuery(GaoPointAccountPage page) {
TimiException.required(page, "not found page");
PageResult<GaoPointAccount> result = new PageResult<>();
result.setTotal(mapper.countByQuery(page));
result.setList(mapper.selectByQuery(page));
fillCustomer(result.getList());
return result;
}
@Override
public GaoPointAccount getByCustomerId(String storeId, String customerId) {
TimiException.required(storeId, "not found storeId");
@@ -36,6 +57,50 @@ public class GaoPointAccountServiceImplement extends AbstractEntityService<GaoPo
return mapper.selectByStoreIdAndCustomerId(storeId, customerId);
}
@Override
public GaoPointAccount getByIdAndStoreId(String storeId, String id) {
TimiException.required(id, "not found accountId");
GaoPointAccount account = mapper.selectByIdAndStoreId(storeId, id);
if (account != null) {
fillCustomer(List.of(account));
}
return account;
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByStoreId(String storeId) {
TimiException.required(storeId, "not found storeId");
GaoPointAccount example = new GaoPointAccount();
example.setStoreId(storeId);
for (GaoPointAccount account : mapper.selectAllByExample(example)) {
super.delete(account.getId());
}
}
private void fillCustomer(List<GaoPointAccount> accountList) {
List<String> customerIdList = accountList.stream()
.map(GaoPointAccount::getCustomerId)
.filter(id -> id != null && !id.isBlank())
.distinct()
.toList();
if (customerIdList.isEmpty()) {
return;
}
Map<String, GaoCustomer> customerMap = customerMapper.selectByIdList(customerIdList).stream()
.collect(Collectors.toMap(GaoCustomer::getId, item -> item));
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList);
for (GaoPointAccount account : accountList) {
GaoCustomer customer = customerMap.get(account.getCustomerId());
if (customer == null) {
continue;
}
customer.setAttachmentList(attachmentMap.get(customer.getId()));
account.setCustomer(customer);
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoPointAccount getOrCreateForUpdate(String storeId, String customerId) {
@@ -50,6 +115,7 @@ public class GaoPointAccountServiceImplement extends AbstractEntityService<GaoPo
account.setCustomerId(customerId);
account.setBalance(0L);
account.setTotalEarned(0L);
account.setTotalConsumed(0L);
account.setTotalRevoked(0L);
account.setTotalAdjusted(0L);
account.setVersion(0L);
@@ -64,8 +130,8 @@ public class GaoPointAccountServiceImplement extends AbstractEntityService<GaoPo
}
@Override
public int updateBalance(String accountId, long delta, long totalEarnedDelta, long totalRevokedDelta, long totalAdjustedDelta) {
public int updateBalance(String accountId, long delta, long totalEarnedDelta, long totalConsumedDelta, long totalRevokedDelta, long totalAdjustedDelta) {
TimiException.required(accountId, "not found accountId");
return mapper.updateBalance(accountId, delta, totalEarnedDelta, totalRevokedDelta, totalAdjustedDelta, Time.now());
return mapper.updateBalance(accountId, delta, totalEarnedDelta, totalConsumedDelta, totalRevokedDelta, totalAdjustedDelta, Time.now());
}
}
@@ -155,6 +155,7 @@ public class GaoPointLedgerServiceImplement extends AbstractEntityService<GaoPoi
long balanceBefore = account.getBalance();
long balanceAfter = Math.addExact(balanceBefore, pointDelta);
long totalEarnedDelta = changeType == GaoPointLedger.ChangeType.EARN ? pointDelta : 0L;
long totalConsumedDelta = changeType == GaoPointLedger.ChangeType.CONSUME ? -pointDelta : 0L;
long totalRevokedDelta = changeType == GaoPointLedger.ChangeType.REVOKE ? -pointDelta : 0L;
long totalAdjustedDelta = changeType == GaoPointLedger.ChangeType.ADJUST ? pointDelta : 0L;
@@ -176,7 +177,7 @@ public class GaoPointLedgerServiceImplement extends AbstractEntityService<GaoPoi
ledger.setOperatorUserId(operatorUserId);
ledger.setOccurredAt(occurredAt);
create(ledger);
int updated = accountService.updateBalance(account.getId(), pointDelta, totalEarnedDelta, totalRevokedDelta, totalAdjustedDelta);
int updated = accountService.updateBalance(account.getId(), pointDelta, totalEarnedDelta, totalConsumedDelta, totalRevokedDelta, totalAdjustedDelta);
TimiException.requiredTrue(0 < updated, "积分余额不足");
logger.setLevel(Logger.Level.INFO);
logger.setResult(ledger.getId());
@@ -55,6 +55,17 @@ public class GaoPointRuleServiceImplement extends AbstractEntityService<GaoPoint
return mapper.selectByIdList(new LinkedHashSet<>(idList));
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByStoreId(String storeId) {
TimiException.required(storeId, "not found storeId");
GaoPointRule example = new GaoPointRule();
example.setStoreId(storeId);
for (GaoPointRule rule : mapper.selectAllByExample(example)) {
delete(rule.getId());
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void sort(List<String> idList) {
@@ -71,6 +71,16 @@ public class GaoStoreBusinessDayServiceImplement extends AbstractEntityService<G
rebuildBusinessDayNo(businessDay.getStoreId(), businessDay.getDateValue());
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByStoreId(String storeId) {
TimiException.required(storeId, "not found storeId");
GaoStoreBusinessDay example = new GaoStoreBusinessDay();
example.setStoreId(storeId);
// 门店整体删除后不再需要重算营业日序号
mapper.deleteAllByExample(example);
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoStoreBusinessDay resolveOpenBusinessDay(String storeId, Integer dateValue) {
@@ -2,9 +2,17 @@ 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.common.entity.Tag;
import com.imyeyu.api.modules.common.service.TagService;
import com.imyeyu.api.modules.gao.entity.GaoStore;
import com.imyeyu.api.modules.gao.mapper.GaoStoreMapper;
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
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.GaoStoreService;
import com.imyeyu.api.modules.gao.service.GaoUserService;
import com.imyeyu.api.modules.gao.util.StoreXSSFBuilder;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
@@ -17,6 +25,7 @@ import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.service.AbstractEntityService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -41,6 +50,13 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
private final LoggerService loggerService;
private final UserLoginService userLoginService;
private final GaoCustomerService customerService;
private final GaoEventService eventService;
private final GaoPointAccountService pointAccountService;
private final GaoPointRuleService pointRuleService;
private final GaoStoreBusinessDayService businessDayService;
private final ObjectProvider<GaoUserService> gaoUserServiceProvider;
private final TagService tagService;
private final GaoStoreMapper mapper;
private final StoreXSSFBuilder storeXSSFBuilder;
@@ -116,6 +132,14 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
try {
logger.setContent(id);
GaoStore store = get(id);
TimiException.required(store, "not found store");
deleteCustomers(store.getId());
deletePointRules(store.getId());
deleteEvents(store.getId());
deleteBusinessDays(store.getId());
deleteGaoUsers(store.getId());
deletePointAccounts(store.getId());
deleteStoreTags(store.getId());
store.setStatus(GaoStore.Status.DELETED);
super.update(store);
super.delete(id);
@@ -135,6 +159,55 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
}
}
/// 删除门店客户及其登记、积分账户、客户标签和附件
///
/// @param storeId 门店 ID
private void deleteCustomers(String storeId) {
customerService.deleteByStoreId(storeId);
}
/// 删除门店积分规则
///
/// @param storeId 门店 ID
private void deletePointRules(String storeId) {
pointRuleService.deleteByStoreId(storeId);
}
/// 删除门店登记事件及角色关联
///
/// @param storeId 门店 ID
private void deleteEvents(String storeId) {
eventService.deleteByStoreId(storeId);
}
/// 删除门店营业日
///
/// @param storeId 门店 ID
private void deleteBusinessDays(String storeId) {
businessDayService.deleteByStoreId(storeId);
}
/// 删除门店用户关系,不删除核心用户
///
/// @param storeId 门店 ID
private void deleteGaoUsers(String storeId) {
gaoUserServiceProvider.getObject().deleteByStoreId(storeId);
}
/// 清理门店下未被客户删除逻辑覆盖的积分账户
///
/// @param storeId 门店 ID
private void deletePointAccounts(String storeId) {
pointAccountService.deleteByStoreId(storeId);
}
/// 删除门店自有标签,客户标签应用由客户删除逻辑处理
///
/// @param storeId 门店 ID
private void deleteStoreTags(String storeId) {
tagService.deleteByOwner(Tag.OwnerType.GAO_STORE, storeId);
}
@Override
public GaoStore getByUserId(String userId) {
TimiException.required(userId, "not found userId");
@@ -276,15 +276,10 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
private Role requireInvitableRole(String inviterUserId, String roleId, String storeId) {
Role role = requireValidRole(roleId, storeId);
List<String> directRoleIdList = userRoleService.listRoleByUserId(ModuleCode.GAO, inviterUserId)
.stream()
.map(Role::getId)
.toList();
boolean adminInviteGlobalManager = isCoreAdmin() && isGlobalManagerRole(role);
boolean descendant = adminInviteGlobalManager || (authorizationScopeService.listManageableRole(inviterUserId, ModuleCode.GAO)
boolean descendant = adminInviteGlobalManager || authorizationScopeService.listManageableRole(inviterUserId, ModuleCode.GAO)
.stream()
.anyMatch(item -> role.getId().equals(item.getId()))
&& !directRoleIdList.contains(role.getId()));
.anyMatch(item -> role.getId().equals(item.getId()));
if (!descendant) {
throw new TimiException(TimiCode.PERMISSION_ERROR, "角色不在可邀请范围内");
}
@@ -40,6 +40,17 @@ public class GaoUserServiceImplement extends AbstractEntityService<GaoUser, Stri
return mapper;
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByStoreId(String storeId) {
TimiException.required(storeId, "not found storeId");
GaoUser example = new GaoUser();
example.setStoreId(storeId);
for (GaoUser gaoUser : mapper.selectAllByExample(example)) {
super.delete(gaoUser.getId());
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(GaoUser gaoUser) {
@@ -0,0 +1,20 @@
package com.imyeyu.api.modules.gao.vo;
import com.imyeyu.java.bean.BasePage;
import lombok.Data;
import lombok.EqualsAndHashCode;
/// GAO 客户积分账户分页查询请求
///
/// @author Codex
/// @since 2026-08-22
@Data
@EqualsAndHashCode(callSuper = true)
public class GaoPointAccountPage extends BasePage {
/// 门店 ID
private String storeId;
/// 客户编码、姓名、电话或拼音关键词
private String keyword;
}
@@ -0,0 +1,13 @@
ALTER TABLE `gao_point_account`
ADD COLUMN `total_consumed` BIGINT NOT NULL DEFAULT 0 COMMENT '累计消耗积分' AFTER `total_earned`;
UPDATE `gao_point_account` account
LEFT JOIN (
SELECT `account_id`, SUM(-`point_delta`) AS `total_consumed`
FROM `gao_point_ledger`
WHERE `change_type` = 'CONSUME'
AND `point_delta` < 0
AND `deleted_at` IS NULL
GROUP BY `account_id`
) ledger ON ledger.`account_id` = account.`id`
SET account.`total_consumed` = COALESCE(ledger.`total_consumed`, 0);
@@ -10,11 +10,58 @@
LIMIT 1
</select>
<select id="selectByIdAndStoreId" resultType="com.imyeyu.api.modules.gao.entity.GaoPointAccount">
SELECT *
FROM `gao_point_account`
WHERE `id` = #{id}
<if test="storeId != null and storeId != ''">
AND `store_id` = #{storeId}
</if>
AND `deleted_at` IS NULL
LIMIT 1
</select>
<sql id="queryWhere">
`a`.`deleted_at` IS NULL
AND `c`.`deleted_at` IS NULL
<if test="storeId != null and storeId != ''">
AND `a`.`store_id` = #{storeId}
</if>
<if test="keyword != null and keyword != ''">
AND (
`c`.`code` LIKE CONCAT('%', #{keyword}, '%')
OR `c`.`name` LIKE CONCAT('%', #{keyword}, '%')
OR `c`.`telephone` LIKE CONCAT('%', #{keyword}, '%')
OR `c`.`name_pinyin_full` LIKE CONCAT(#{keyword}, '%')
OR `c`.`name_pinyin_initial` LIKE CONCAT(#{keyword}, '%')
OR `c`.`name_pinyin_mixed` LIKE CONCAT(#{keyword}, '%')
)
</if>
</sql>
<select id="countByQuery" resultType="long">
SELECT COUNT(1)
FROM `gao_point_account` `a`
INNER JOIN `gao_customer` `c` ON `c`.`id` = `a`.`customer_id` AND `c`.`store_id` = `a`.`store_id`
WHERE
<include refid="queryWhere"/>
</select>
<select id="selectByQuery" resultType="com.imyeyu.api.modules.gao.entity.GaoPointAccount">
SELECT `a`.*
FROM `gao_point_account` `a`
INNER JOIN `gao_customer` `c` ON `c`.`id` = `a`.`customer_id` AND `c`.`store_id` = `a`.`store_id`
WHERE
<include refid="queryWhere"/>
ORDER BY `a`.`created_at` DESC, `a`.`id` DESC
LIMIT #{offset}, #{limit}
</select>
<insert id="insertIfAbsent">
INSERT INTO `gao_point_account` (
`id`, `store_id`, `customer_id`, `balance`, `total_earned`, `total_revoked`, `total_adjusted`, `version`, `created_at`, `updated_at`, `deleted_at`
`id`, `store_id`, `customer_id`, `balance`, `total_earned`, `total_consumed`, `total_revoked`, `total_adjusted`, `version`, `created_at`, `updated_at`, `deleted_at`
) VALUES (
#{account.id}, #{account.storeId}, #{account.customerId}, #{account.balance}, #{account.totalEarned}, #{account.totalRevoked}, #{account.totalAdjusted}, #{account.version}, #{account.createdAt}, #{account.updatedAt}, #{account.deletedAt}
#{account.id}, #{account.storeId}, #{account.customerId}, #{account.balance}, #{account.totalEarned}, #{account.totalConsumed}, #{account.totalRevoked}, #{account.totalAdjusted}, #{account.version}, #{account.createdAt}, #{account.updatedAt}, #{account.deletedAt}
)
ON DUPLICATE KEY UPDATE `id` = `id`
</insert>
@@ -33,6 +80,7 @@
SET
`balance` = `balance` + #{delta},
`total_earned` = `total_earned` + #{totalEarnedDelta},
`total_consumed` = `total_consumed` + #{totalConsumedDelta},
`total_revoked` = `total_revoked` + #{totalRevokedDelta},
`total_adjusted` = `total_adjusted` + #{totalAdjustedDelta},
`version` = `version` + 1,