add GaoPackage

This commit is contained in:
Timi
2026-09-01 01:16:52 +08:00
parent b4b6a13f8d
commit eeb7b5b32e
24 changed files with 272 additions and 49 deletions
@@ -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:";
@@ -158,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());
@@ -257,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,
@@ -73,7 +73,7 @@ public enum GaoPermissionCode implements BuiltinPermissionCode {
CUSTOMER_PACKAGE_CANCEL("CUSTOMER_PACKAGE:CANCEL", "客户套餐取消", true, true),
CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和产品定制", true, true),
CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和权益配额调整", true, true),
PACKAGE_DELIVERY_CREATE("PACKAGE_DELIVERY:CREATE", "套餐发放创建", 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;
@@ -4,7 +4,10 @@ import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
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.GaoPackageConfigService;
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException;
@@ -32,6 +35,7 @@ import java.util.Map;
public class GaoEventController {
private final GaoEventService service;
private final GaoPackageConfigService packageConfigService;
private final GaoStoreScopeService storeScopeService;
@AOPLog
@@ -69,15 +73,38 @@ public class GaoEventController {
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(storeScopeService.resolveStoreId(storeId));
List<GaoEvent> result = service.listValid(storeScopeService.resolveStoreId(storeId));
attachPackageConfig(result);
return result;
}
@AOPLog
@@ -90,6 +117,7 @@ public class GaoEventController {
storeScopeService.checkStoreId(event.getStoreId());
event.setRoleCodeList(service.listRoleCode(event.getId()));
service.attachOptionGroupList(List.of(event));
attachPackageConfig(List.of(event));
return event;
}
@@ -13,10 +13,10 @@ import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
import com.imyeyu.api.modules.gao.service.GaoEventService;
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.gao.vo.GaoEventOptionStatView;
import com.imyeyu.api.modules.user.service.UserService;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException;
@@ -114,6 +114,7 @@ public class GaoEventRecordController {
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()));
}
@@ -106,6 +106,13 @@ public class GaoEvent extends UUIDEntity {
/// 业务类型
private BizType bizType;
/// 购买套餐事件绑定的套餐配置 ID
private String packageConfigId;
/// 购买套餐事件绑定的套餐配置
@Transient
private GaoPackageConfig packageConfig;
/// 登记事件名称
private String name;
@@ -87,6 +87,9 @@ public class GaoEventRecord extends UUIDEntity {
/// 营业日递增序号
private Long businessDayNo;
/// 最后修改用户 ID
private String updatedBy;
@Transient
private GaoCustomer customer;
@@ -99,6 +102,10 @@ public class GaoEventRecord extends UUIDEntity {
@Transient
private User verifier;
/// 最后修改用户
@Transient
private User updater;
@Transient
private List<Attachment> attachmentList;
@@ -4,6 +4,7 @@ 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;
@@ -25,4 +26,7 @@ public interface GaoEventMapper extends BaseMapper<GaoEvent, String> {
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);
}
@@ -12,6 +12,7 @@ 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;
@@ -21,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);
/// 查询事件中已携带数值的登记记录数量
@@ -50,8 +58,9 @@ public interface GaoEventRecordMapper extends BaseMapper<GaoEventRecord, String>
/// @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);
int verify(@Param("id") String id, @Param("verifierUserId") String verifierUserId, @Param("verifiedAt") Long verifiedAt, @Param("updatedBy") String updatedBy);
/// 按门店和登记日期统计登记记录数
///
@@ -22,6 +22,12 @@ public interface GaoPackageConfigService extends BaseService<GaoPackageConfig, S
/// 查询有效可购买套餐
List<GaoPackageConfig> listValid(String storeId);
/// 批量查询套餐配置
///
/// @param idList 套餐配置 ID 列表
/// @return 按套餐配置 ID 映射的配置
Map<String, GaoPackageConfig> mapByIdList(Collection<String> idList);
/// 查询套餐配置产品明细
///
/// @param packageConfigId 套餐配置 ID
@@ -171,23 +171,25 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
TimiException.required(request, "not found purchase request");
TimiException.required(request.getRequestNo(), "not found requestNo");
request.setRequestNo(request.getRequestNo().trim());
TimiException.required(request.getPackageConfigId(), "not found packageConfigId");
TimiException.required(request.getEventId(), "not found eventId");
TimiException.required(request.getCustomerId(), "not found customerId");
TimiException.required(request.getSaleAmount(), "not found saleAmount");
TimiException.requiredTrue(0 <= request.getSaleAmount(), "成交价不能为负数");
GaoPackageConfig config = packageConfigService.get(request.getPackageConfigId());
if (config == null) {
GaoCustomerPackage existing = mapper.selectByRequestNoAnyStore(request.getRequestNo());
if (existing != null) {
storeScopeService.checkStoreId(existing.getStoreId());
verifyPurchaseIdempotency(existing, request);
return existing;
}
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));
}
TimiException.required(config, "not found package config");
storeScopeService.checkStoreId(config.getStoreId());
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);
@@ -199,17 +201,12 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
TimiException.requiredTrue(config.getStatus() == GaoPackageConfig.Status.ACTIVE, "套餐配置不可购买");
TimiException.requiredTrue(config.getBeginAt() == null || config.getBeginAt() <= now, "套餐配置尚未生效");
TimiException.requiredTrue(config.getEndAt() == null || now <= config.getEndAt(), "套餐配置已过期");
List<GaoPackageConfigItem> configItemList = packageConfigService.listItemByPackageConfigId(config.getId());
GaoCustomer customer = validateCustomer(request.getCustomerId(), config.getStoreId());
GaoEvent event = validatePurchaseEvent(request.getEventId(), config.getStoreId());
List<GaoCustomerPackagePurchaseItem> itemList = new java.util.ArrayList<>(TimiJava.defaultIfNull(request.getItemList(), List.of()));
verifyPurchaseItems(itemList);
itemList.sort(java.util.Comparator.comparing(GaoCustomerPackagePurchaseItem::getSort));
boolean canOverride = permissionChecker.hasAny(ModuleCode.GAO, com.imyeyu.api.modules.gao.bean.GaoPermissionCode.CUSTOMER_PACKAGE_PRICE_OVERRIDE.getValue());
if (!canOverride) {
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()), "普通员工不能修改套餐成交价");
verifySameAsConfig(itemList, configItemList);
verifySameQuantityAsConfig(itemList, configItemList);
}
GaoEventRecord eventRecord = new GaoEventRecord();
@@ -299,14 +296,26 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
return customer;
}
private GaoEvent validatePurchaseEvent(String eventId, String storeId) {
private GaoEvent validatePurchaseEvent(String eventId) {
GaoEvent event = eventService.get(eventId);
TimiException.required(event, "not found purchase event");
TimiException.requiredTrue(storeId.equals(event.getStoreId()), "购买事件和套餐不属于同一门店");
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<>();
@@ -326,20 +335,27 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
}
}
private void verifySameAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
TimiException.requiredTrue(itemList.size() == configItemList.size(), "普通员工不能修改套餐产品明细");
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.getTotalQuantity().compareTo(configItem.getDefaultQuantity()) == 0
&& item.getSort().equals(configItem.getSort()), "普通员工不能修改套餐产品明细");
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.getPackageConfigId().equals(request.getPackageConfigId())
&& existing.getCustomerId().equals(request.getCustomerId())
TimiException.requiredTrue(existing.getCustomerId().equals(request.getCustomerId())
&& existing.getSaleAmount().equals(request.getSaleAmount()), "requestNo 已被其他购买请求使用");
GaoEventRecord eventRecord = eventRecordService.get(existing.getPurchaseEventRecordId());
TimiException.required(eventRecord, "购买事件登记不存在");
@@ -354,7 +370,9 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
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()), "requestNo 对应购买请求内容不一致");
TimiException.requiredTrue(existingItem.getTotalQuantity().compareTo(item.getTotalQuantity()) == 0
&& existingItem.getSort().equals(item.getSort())
&& sameText(existingItem.getRemark(), item.getRemark()), "requestNo 对应购买请求内容不一致");
}
}
@@ -139,7 +139,8 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
GaoEventRecord record = get(id);
TimiException.required(record, "not found event record");
TimiException.requiredTrue(record.getVerificationStatus() == GaoEventRecord.VerificationStatus.PENDING, "登记记录无需核销或已核销");
int updated = mapper.verify(id, userLoginService.getRequireLoginUserId(), Time.now());
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);
@@ -175,7 +176,6 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
return createInternal(record, true);
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
private GaoEventRecord createInternal(GaoEventRecord record, boolean packagePurchase) {
TimiException.required(record, "not found record");
@@ -224,6 +224,7 @@ 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);
@@ -318,6 +319,7 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
verifyRecordAttachment(event, record.getAttachmentList());
checkEventPermission(event);
dbRecord.setRemark(record.getRemark());
dbRecord.setUpdatedBy(userLoginService.getRequireLoginUserId());
super.update(dbRecord);
if (canonicalOptionList != null) {
GaoEventRecordOption optionExample = new GaoEventRecordOption();
@@ -6,12 +6,14 @@ 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;
@@ -57,6 +59,7 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
private final GaoEventOptionMapper optionMapper;
private final GaoEventRecordMapper eventRecordMapper;
private final GaoEventRoleRelationMapper roleRelationMapper;
private final GaoPackageConfigService packageConfigService;
@Override
protected BaseMapper<GaoEvent, String> mapper() {
@@ -130,6 +133,10 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
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());
@@ -317,6 +324,12 @@ public class GaoEventServiceImplement extends AbstractEntityService<GaoEvent, St
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) {
@@ -7,6 +7,7 @@ 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;
@@ -37,6 +38,7 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
private final GaoPackageConfigMapper mapper;
private final GaoPackageConfigItemMapper itemMapper;
private final GaoEventMapper eventMapper;
private final UserLoginService userLoginService;
private final LoggerService loggerService;
@@ -60,6 +62,15 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
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");
@@ -134,6 +145,7 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
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;
});
@@ -182,7 +182,7 @@ public class GaoPackageDeliveryServiceImplement extends AbstractEntityService<Ga
BigDecimal reservedBefore = decimal(packageItem.getReservedQuantity());
BigDecimal deliveredBefore = decimal(packageItem.getDeliveredQuantity());
BigDecimal available = decimal(packageItem.getTotalQuantity()).subtract(reservedBefore).subtract(deliveredBefore);
TimiException.requiredTrue(0 < requestItem.getQuantity().compareTo(available), "产品可发数量不足");
TimiException.requiredTrue(0 <= available.compareTo(requestItem.getQuantity()), "产品可发数量不足");
int updated = customerPackageService.updateItemReservedQuantity(packageItem.getId(), requestItem.getQuantity());
TimiException.requiredTrue(0 < updated, "产品可发数量不足");
GaoPackageDeliveryItem savedItem = copyDeliveryItem(requestItem);
@@ -21,16 +21,13 @@ public class GaoCustomerPackagePurchaseRequest {
/// 客户 ID
private String customerId;
/// 套餐配置 ID
private String packageConfigId;
/// 实际成交价,单位分
/// 实际成交价,单位分,空为事件绑定套餐的标准价
private Long saleAmount;
/// 购买时间
private Long purchasedAt;
/// 最终产品明细
/// 最终产品明细,空为事件绑定套餐的默认产品明细
private List<GaoCustomerPackagePurchaseItem> itemList;
/// 购买照片
@@ -206,6 +206,7 @@ public class InitBuiltinUser implements ApplicationRunner {
private void bindBuiltinRoleParent(Map<String, Role> roleMap) {
setRoleParent(roleMap, ModuleCode.CORE, CoreRoleCode.ADMIN.name(), ModuleCode.CORE, CoreRoleCode.SYSTEM.name());
setRoleParent(roleMap, ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name(), ModuleCode.CORE, CoreRoleCode.ADMIN.name());
setRoleParent(roleMap, ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name(), ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name());
}
/// 按权限定义补齐 SYSTEM 和 ADMIN 的默认权限