Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d22df698b6 | ||
|
|
cb6051cc41 | ||
|
|
1d1009b121 | ||
|
|
94f3fbb867 | ||
|
|
1771bf9909 | ||
|
|
fc4d55517b | ||
|
|
ec72a99d74 | ||
|
|
a431db6e6e | ||
|
|
9cfb31b015 | ||
|
|
15d69f9361 | ||
|
|
b000390a75 | ||
|
|
1b9f92caf5 | ||
|
|
7b08641acc | ||
|
|
66c13062b2 | ||
|
|
2b4089ef59 | ||
|
|
cdf8a426e9 | ||
|
|
a2d69fe721 | ||
|
|
19db357b44 | ||
|
|
07ffef3e2a | ||
|
|
20c3e1f422 | ||
|
|
f93ec3cd4d | ||
|
|
3cdac98832 | ||
|
|
c122c35376 | ||
|
|
3b19d9e930 | ||
|
|
2ea06fb5c0 | ||
|
|
00bb300755 | ||
|
|
ea44dd5c0e | ||
|
|
818207d6c9 | ||
|
|
af76eb43b8 | ||
|
|
5fd11e9caf | ||
|
|
d920b8630d |
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<groupId>com.imyeyu.timiserverapi</groupId>
|
<groupId>com.imyeyu.timiserverapi</groupId>
|
||||||
<artifactId>TimiServerAPI</artifactId>
|
<artifactId>TimiServerAPI</artifactId>
|
||||||
<version>1.0.34</version>
|
<version>1.0.41</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>TimiServerAPI</name>
|
<name>TimiServerAPI</name>
|
||||||
<description>imyeyu.com API</description>
|
<description>imyeyu.com API</description>
|
||||||
@@ -215,6 +215,11 @@
|
|||||||
<artifactId>tinypinyin</artifactId>
|
<artifactId>tinypinyin</artifactId>
|
||||||
<version>2.0.3</version>
|
<version>2.0.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.hankcs</groupId>
|
||||||
|
<artifactId>hanlp</artifactId>
|
||||||
|
<version>portable-1.8.4</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.imyeyu.font.icon</groupId>
|
<groupId>com.imyeyu.font.icon</groupId>
|
||||||
<artifactId>timi-icon-font</artifactId>
|
<artifactId>timi-icon-font</artifactId>
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.imyeyu.api.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启用配置注解
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2023-07-15 10:00
|
|
||||||
*/
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface EnableSetting {
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-27 14:45
|
|
||||||
*/
|
|
||||||
enum Logic {
|
|
||||||
|
|
||||||
AND,
|
|
||||||
|
|
||||||
OR
|
|
||||||
}
|
|
||||||
|
|
||||||
Logic logic() default Logic.AND;
|
|
||||||
|
|
||||||
/** @return 未启用配置时响应消息语言映射键 */
|
|
||||||
String message() default "service.offline";
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package com.imyeyu.api.annotation;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.lang.NonNull;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.method.HandlerMethod;
|
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启用配置注解处理器
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2023-07-15 10:01
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
|
||||||
public class EnableSettingInterceptor implements HandlerInterceptor {
|
|
||||||
|
|
||||||
private final SettingService service;
|
|
||||||
|
|
||||||
public boolean preHandle(@NonNull HttpServletRequest req, @NonNull HttpServletResponse resp, @NonNull Object handler) throws InvocationTargetException, IllegalAccessException {
|
|
||||||
if (handler instanceof HandlerMethod handlerMethod) {
|
|
||||||
EnableSetting annotation = handlerMethod.getMethodAnnotation(EnableSetting.class);
|
|
||||||
if (annotation == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
EnableSetting.Logic logic = annotation.logic();
|
|
||||||
List<Boolean> valueList = new ArrayList<>();
|
|
||||||
for (Method method : annotation.getClass().getMethods()) {
|
|
||||||
if (method.getName().equals("message")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Enum<?>[] enums = (Enum<?>[]) method.invoke(annotation);
|
|
||||||
for (Enum<?> anEnum : enums) {
|
|
||||||
valueList.add(service.getSystem(anEnum).isTrue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (logic == EnableSetting.Logic.AND) {
|
|
||||||
return valueList.stream().allMatch(Boolean::booleanValue);
|
|
||||||
} else {
|
|
||||||
return valueList.stream().anyMatch(Boolean::booleanValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.imyeyu.api.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// @author 夜雨
|
||||||
|
/// @since 2026-09-08 15:22
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface SettingBizType {
|
||||||
|
|
||||||
|
String value();
|
||||||
|
}
|
||||||
@@ -45,6 +45,11 @@ public enum CorePermissionCode implements BuiltinPermissionCode {
|
|||||||
TAG_UPDATE("TAG:UPDATE", "标签修改", false, true),
|
TAG_UPDATE("TAG:UPDATE", "标签修改", false, true),
|
||||||
TAG_DELETE("TAG:DELETE", "标签删除", false, true),
|
TAG_DELETE("TAG:DELETE", "标签删除", false, true),
|
||||||
|
|
||||||
|
ARTICLE_CREATE("ARTICLE:CREATE", "文章创建", false, true),
|
||||||
|
ARTICLE_READ("ARTICLE:READ", "文章读取", false, true),
|
||||||
|
ARTICLE_UPDATE("ARTICLE:UPDATE", "文章修改", false, true),
|
||||||
|
ARTICLE_DELETE("ARTICLE:DELETE", "文章删除", false, true),
|
||||||
|
|
||||||
USER_CREATE("USER:CREATE", "用户创建", false, true),
|
USER_CREATE("USER:CREATE", "用户创建", false, true),
|
||||||
USER_READ("USER:READ", "用户读取", false, true),
|
USER_READ("USER:READ", "用户读取", false, true),
|
||||||
USER_UPDATE("USER:UPDATE", "用户修改", false, true),
|
USER_UPDATE("USER:UPDATE", "用户修改", false, true),
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.imyeyu.api.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.annotation.SettingBizType;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import com.imyeyu.utils.Text;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// @author 夜雨
|
||||||
|
/// @since 2026-09-08 15:18
|
||||||
|
public interface SettingEnum {
|
||||||
|
|
||||||
|
default String getBizType() {
|
||||||
|
Class<? extends SettingEnum> implEnum = getClass();
|
||||||
|
SettingBizType bizType = implEnum.getAnnotation(SettingBizType.class);
|
||||||
|
if (bizType == null) {
|
||||||
|
return Text.bizClassName(implEnum);
|
||||||
|
}
|
||||||
|
return bizType.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
String name();
|
||||||
|
|
||||||
|
String getDefaultValue();
|
||||||
|
|
||||||
|
Setting.ValueType getValueType();
|
||||||
|
}
|
||||||
@@ -32,4 +32,22 @@ public class ThreadPoolConfig {
|
|||||||
executor.initialize();
|
executor.initialize();
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 创建操作审计线程池
|
||||||
|
///
|
||||||
|
/// @return 操作审计线程池
|
||||||
|
@Bean(name = "auditTaskExecutor")
|
||||||
|
public ThreadPoolTaskExecutor auditTaskExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(4);
|
||||||
|
executor.setMaxPoolSize(8);
|
||||||
|
executor.setQueueCapacity(2000);
|
||||||
|
executor.setKeepAliveSeconds(60);
|
||||||
|
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||||
|
executor.setAwaitTerminationSeconds(60);
|
||||||
|
executor.setThreadNamePrefix("audit-task-executor-");
|
||||||
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.MapperFeature;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import com.imyeyu.api.annotation.EnableSettingInterceptor;
|
|
||||||
import com.imyeyu.api.annotation.RequestRateLimitInterceptor;
|
import com.imyeyu.api.annotation.RequestRateLimitInterceptor;
|
||||||
import com.imyeyu.api.annotation.RequireCorePermissionInterceptor;
|
import com.imyeyu.api.annotation.RequireCorePermissionInterceptor;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermissionInterceptor;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermissionInterceptor;
|
||||||
@@ -40,7 +39,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
private final ObjectMapper jackson;
|
private final ObjectMapper jackson;
|
||||||
private final SystemAPIInterceptor systemAPIInterceptor;
|
private final SystemAPIInterceptor systemAPIInterceptor;
|
||||||
private final JournalAPIInterceptor journalAPIInterceptor;
|
private final JournalAPIInterceptor journalAPIInterceptor;
|
||||||
private final EnableSettingInterceptor enableSettingInterceptor;
|
|
||||||
private final RequestRateLimitInterceptor requestRateLimitInterceptor;
|
private final RequestRateLimitInterceptor requestRateLimitInterceptor;
|
||||||
private final RequireGaoPermissionInterceptor requireGaoPermissionInterceptor;
|
private final RequireGaoPermissionInterceptor requireGaoPermissionInterceptor;
|
||||||
private final RequireCorePermissionInterceptor requireCorePermissionInterceptor;
|
private final RequireCorePermissionInterceptor requireCorePermissionInterceptor;
|
||||||
@@ -54,7 +52,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(systemAPIInterceptor).addPathPatterns(SystemAPIInterceptor.PATH);
|
registry.addInterceptor(systemAPIInterceptor).addPathPatterns(SystemAPIInterceptor.PATH);
|
||||||
registry.addInterceptor(journalAPIInterceptor).addPathPatterns(JournalAPIInterceptor.PATH);
|
registry.addInterceptor(journalAPIInterceptor).addPathPatterns(JournalAPIInterceptor.PATH);
|
||||||
registry.addInterceptor(enableSettingInterceptor).addPathPatterns("/**");
|
|
||||||
registry.addInterceptor(requireCorePermissionInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(requireCorePermissionInterceptor).addPathPatterns("/**");
|
||||||
registry.addInterceptor(requireGaoPermissionInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(requireGaoPermissionInterceptor).addPathPatterns("/**");
|
||||||
registry.addInterceptor(requestRateLimitInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(requestRateLimitInterceptor).addPathPatterns("/**");
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.bean;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置模块
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 00:59
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum SettingModule {
|
|
||||||
|
|
||||||
COMMON(Setting.Module.Common.class),
|
|
||||||
|
|
||||||
DOMAIN(Setting.Module.Domain.class),
|
|
||||||
|
|
||||||
FOREVER_MC(Setting.Module.ForeverMC.class),
|
|
||||||
|
|
||||||
MULTILINGUAL(Setting.Module.Multilingual.class),
|
|
||||||
|
|
||||||
GITEA(Setting.Module.Gitea.class),
|
|
||||||
|
|
||||||
MUSIC(Setting.Module.Music.class),
|
|
||||||
|
|
||||||
JOURNAL(Setting.Module.Journal.class),
|
|
||||||
|
|
||||||
TEMP_FILE(Setting.Module.TempFile.class),
|
|
||||||
|
|
||||||
USER(Setting.Module.User.class),
|
|
||||||
|
|
||||||
SYSTEM(Setting.Module.System.class),
|
|
||||||
|
|
||||||
SYSTEM_STATUS(Setting.Module.SystemStatus.class);
|
|
||||||
|
|
||||||
/** 模块枚举类 */
|
|
||||||
final Class<? extends Enum<?>> clazz;
|
|
||||||
|
|
||||||
public static <T extends Enum<?>> SettingModule fromClass(Class<T> clazz) {
|
|
||||||
for (SettingModule value : values()) {
|
|
||||||
if (value.clazz.equals(clazz)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new TimiException(TimiCode.ERROR, "not found System Module: %s".formatted(clazz.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.imyeyu.api.modules.common.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.annotation.SettingBizType;
|
||||||
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-04-24 14:47
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@SettingBizType("TEMP_FILE")
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum TempFileSetting implements SettingEnum {
|
||||||
|
|
||||||
|
/** 启用服务 */
|
||||||
|
ENABLE(Boolean.TRUE.toString(), Setting.ValueType.BOOLEAN),
|
||||||
|
|
||||||
|
/** 最小缓存时长 */
|
||||||
|
TTL_MIN("1h", Setting.ValueType.TIME),
|
||||||
|
|
||||||
|
/** 默认缓存时长 */
|
||||||
|
TTL_DEFAULT("3d", Setting.ValueType.TIME),
|
||||||
|
|
||||||
|
/** 最大缓存时长 */
|
||||||
|
TTL_MAX("7d", Setting.ValueType.TIME),
|
||||||
|
|
||||||
|
/** 已过期文件残留时长 */
|
||||||
|
RESIDUE_TTL("90d", Setting.ValueType.TIME),
|
||||||
|
|
||||||
|
/** 每个 IP 限制有效临时文件容量 */
|
||||||
|
LIMIT_SIZE_OF_IP("10 GB", Setting.ValueType.BYTE_SIZE);
|
||||||
|
|
||||||
|
final String defaultValue;
|
||||||
|
|
||||||
|
final Setting.ValueType valueType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.imyeyu.api.modules.common.controller;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
import com.imyeyu.api.annotation.RequireCorePermission;
|
||||||
|
import com.imyeyu.api.bean.CorePermissionCode;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Article;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.api.modules.common.service.ArticleService;
|
||||||
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import com.imyeyu.spring.bean.Page;
|
||||||
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import com.imyeyu.spring.util.ResponseView;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// 文章接口
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-09
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/article")
|
||||||
|
public class ArticleController {
|
||||||
|
|
||||||
|
private final ArticleService service;
|
||||||
|
private final AttachmentService attachmentService;
|
||||||
|
|
||||||
|
/// 查询文章列表
|
||||||
|
///
|
||||||
|
/// @param page 分页参数
|
||||||
|
/// @return 文章分页
|
||||||
|
@JsonView(ResponseView.Public.class)
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireCorePermission(CorePermissionCode.ARTICLE_READ)
|
||||||
|
@PostMapping("/list")
|
||||||
|
public PageResult<Article> list(@RequestBody Page<Article> page) {
|
||||||
|
return service.page(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询文章详情
|
||||||
|
///
|
||||||
|
/// @param id 文章 ID
|
||||||
|
/// @return 文章详情
|
||||||
|
@JsonView(ResponseView.Public.class)
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequestMapping("/detail")
|
||||||
|
public Article detail(@RequestParam String id) {
|
||||||
|
Article article = service.get(id);
|
||||||
|
article.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.ARTICLE, article.getId()));
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 创建文章
|
||||||
|
///
|
||||||
|
/// @param article 文章
|
||||||
|
/// @return 创建后的文章
|
||||||
|
@AOPLog
|
||||||
|
@JsonView(ResponseView.Public.class)
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireCorePermission(CorePermissionCode.ARTICLE_CREATE)
|
||||||
|
@PostMapping("/create")
|
||||||
|
public Article create(@RequestBody Article article) {
|
||||||
|
service.create(article);
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新文章
|
||||||
|
///
|
||||||
|
/// @param article 文章
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireCorePermission(CorePermissionCode.ARTICLE_UPDATE)
|
||||||
|
@PostMapping("/update")
|
||||||
|
public void update(@RequestBody Article article) {
|
||||||
|
service.update(article);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除文章
|
||||||
|
///
|
||||||
|
/// @param id 文章 ID
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireCorePermission(CorePermissionCode.ARTICLE_DELETE)
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public void delete(@RequestParam String id) {
|
||||||
|
service.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.common.controller;
|
package com.imyeyu.api.modules.common.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.vo.ClientLogRequest;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
@@ -29,7 +30,9 @@ public class LoggerController {
|
|||||||
@AOPLog
|
@AOPLog
|
||||||
@RequiredToken
|
@RequiredToken
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public void create(@Valid @RequestBody Logger logger) {
|
public void create(@Valid @RequestBody ClientLogRequest request) {
|
||||||
|
Logger logger = Logger.info(Logger.Module.COMMON, "CLIENT_LOG");
|
||||||
|
logger.setContent(request.getContent());
|
||||||
service.create(logger);
|
service.create(logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,6 +58,14 @@ public class NotifyController {
|
|||||||
@RequiredToken
|
@RequiredToken
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public Notify create(@RequestBody Notify notify) {
|
public Notify create(@RequestBody Notify notify) {
|
||||||
|
String sendFrom = userLoginService.isLogged() ? userLoginService.getRequireLoginUserId() : null;
|
||||||
|
notify.setCreatedBy(sendFrom);
|
||||||
|
for (NotifyDetail detail : notify.getDetailList()) {
|
||||||
|
detail.setNotifyId(notify.getId());
|
||||||
|
if (TimiJava.isNotEmpty(sendFrom)) {
|
||||||
|
detail.setSendFrom(sendFrom);
|
||||||
|
}
|
||||||
|
}
|
||||||
service.create(notify);
|
service.create(notify);
|
||||||
return notify;
|
return notify;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -26,11 +30,30 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/system/setting")
|
@RequestMapping("/setting")
|
||||||
public class SettingController {
|
public class SettingController {
|
||||||
|
|
||||||
private final SettingService service;
|
private final SettingService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询公开配置映射
|
||||||
|
*
|
||||||
|
* @param bizType 业务类型
|
||||||
|
* @param bizId 业务 ID,可为空
|
||||||
|
* @param code 配置代码列表,可为空
|
||||||
|
* @return 公开配置映射
|
||||||
|
*/
|
||||||
|
@JsonView(ResponseView.Public.class)
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequestMapping("/map")
|
||||||
|
public Map<String, Setting> map(
|
||||||
|
@RequestParam String bizType,
|
||||||
|
@RequestParam(required = false) String bizId,
|
||||||
|
@RequestParam(required = false) List<String> code
|
||||||
|
) {
|
||||||
|
return service.mapPublic(bizType, bizId, code);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询配置列表
|
* 查询配置列表
|
||||||
*
|
*
|
||||||
@@ -53,6 +76,7 @@ public class SettingController {
|
|||||||
*/
|
*/
|
||||||
@JsonView(ResponseView.Admin.class)
|
@JsonView(ResponseView.Admin.class)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
@RequireCorePermission(CorePermissionCode.SETTING_READ)
|
||||||
@PostMapping("/detail")
|
@PostMapping("/detail")
|
||||||
public Setting detail(@RequestBody String id) {
|
public Setting detail(@RequestBody String id) {
|
||||||
return service.get(id);
|
return service.get(id);
|
||||||
@@ -66,6 +90,7 @@ public class SettingController {
|
|||||||
*/
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@JsonView(ResponseView.Admin.class)
|
@JsonView(ResponseView.Admin.class)
|
||||||
|
@RequireCorePermission(CorePermissionCode.SETTING_CREATE)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public Setting create(@RequestBody Setting setting) {
|
public Setting create(@RequestBody Setting setting) {
|
||||||
@@ -80,6 +105,7 @@ public class SettingController {
|
|||||||
*/
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
@RequireCorePermission(CorePermissionCode.SETTING_UPDATE)
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
public void update(@RequestBody Setting setting) {
|
public void update(@RequestBody Setting setting) {
|
||||||
service.update(setting);
|
service.update(setting);
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class Article extends UUIDEntity implements SettingSupport {
|
|||||||
/// GAO 门店日记
|
/// GAO 门店日记
|
||||||
GAO_STORE_DIARY,
|
GAO_STORE_DIARY,
|
||||||
|
|
||||||
|
GAO_SYSTEM,
|
||||||
|
|
||||||
/// 反馈
|
/// 反馈
|
||||||
FEEDBACK
|
FEEDBACK
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.common.entity;
|
package com.imyeyu.api.modules.common.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
import com.imyeyu.spring.entity.UUIDEntity;
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@@ -39,4 +40,7 @@ public class GroovyScript extends UUIDEntity {
|
|||||||
|
|
||||||
/// 当前发布版本 ID
|
/// 当前发布版本 ID
|
||||||
private String publishedVersionId;
|
private String publishedVersionId;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private GroovyScriptVersion latest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ public class Notify extends UUIDEntity {
|
|||||||
/** 业务类型 */
|
/** 业务类型 */
|
||||||
private String bizType;
|
private String bizType;
|
||||||
|
|
||||||
/** 提醒业务类型 */
|
/** 业务提醒类型 */
|
||||||
private String bizRemindedType;
|
private String bizRemindedType;
|
||||||
|
|
||||||
/** 提醒业务 ID */
|
/** 业务提醒查重键 */
|
||||||
private String bizRemindedId;
|
private String bizRemindedKey;
|
||||||
|
|
||||||
/** 参数 */
|
/** 参数 */
|
||||||
private String args;
|
private String args;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.imyeyu.api.TimiServerAPI;
|
import com.imyeyu.api.TimiServerAPI;
|
||||||
import com.imyeyu.api.modules.common.bean.SettingModule;
|
|
||||||
import com.imyeyu.spring.annotation.table.Transient;
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
import com.imyeyu.spring.entity.UUIDEntity;
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
import com.imyeyu.spring.util.ResponseView;
|
import com.imyeyu.spring.util.ResponseView;
|
||||||
@@ -62,6 +61,9 @@ public class Setting extends UUIDEntity {
|
|||||||
/** 时间 */
|
/** 时间 */
|
||||||
TIME,
|
TIME,
|
||||||
|
|
||||||
|
/// 字节大小
|
||||||
|
BYTE_SIZE,
|
||||||
|
|
||||||
/** 时长 */
|
/** 时长 */
|
||||||
DURATION,
|
DURATION,
|
||||||
|
|
||||||
@@ -102,16 +104,6 @@ public class Setting extends UUIDEntity {
|
|||||||
@Transient
|
@Transient
|
||||||
private Enum<?> keyRaw;
|
private Enum<?> keyRaw;
|
||||||
|
|
||||||
public Setting(Enum<?> key) {
|
|
||||||
setKeyRaw(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Setting(Enum<?> key, Object value, ValueType valueType) {
|
|
||||||
setKeyRaw(key);
|
|
||||||
this.value = value.toString();
|
|
||||||
this.valueType = valueType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以字符串形式返回配置值
|
* 以字符串形式返回配置值
|
||||||
*
|
*
|
||||||
@@ -221,298 +213,4 @@ public class Setting extends UUIDEntity {
|
|||||||
throw new IllegalArgumentException("setting value is not valid json: %s".formatted(value), e);
|
throw new IllegalArgumentException("setting value is not valid json: %s".formatted(value), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyRaw(Enum<?> key) {
|
|
||||||
this.keyRaw = key;
|
|
||||||
this.bizType = SettingModule.fromClass(key.getClass()).toString();
|
|
||||||
this.code = key.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean is(Enum<?> key) {
|
|
||||||
return SettingModule.fromClass(key.getClass()).toString().equals(bizType) && key.toString().equals(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:23
|
|
||||||
*/
|
|
||||||
public static final class Module {
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 15:39
|
|
||||||
*/
|
|
||||||
public enum Common {
|
|
||||||
|
|
||||||
/** 微信收款码 */
|
|
||||||
WECHAT_RECEIVE_QR_CODE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 域名
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-26 17:18
|
|
||||||
*/
|
|
||||||
public enum Domain {
|
|
||||||
|
|
||||||
ROOT,
|
|
||||||
|
|
||||||
API,
|
|
||||||
|
|
||||||
GIT,
|
|
||||||
|
|
||||||
BLOG,
|
|
||||||
|
|
||||||
SPACE,
|
|
||||||
|
|
||||||
FOREVER_MC
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 15:14
|
|
||||||
*/
|
|
||||||
public enum Blog {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 15:03
|
|
||||||
*/
|
|
||||||
public enum User {
|
|
||||||
|
|
||||||
/** 启用登录 */
|
|
||||||
ENABLE_LOGIN,
|
|
||||||
|
|
||||||
/** 启用注册 */
|
|
||||||
ENABLE_REGISTER,
|
|
||||||
|
|
||||||
/** 允许数据更新 */
|
|
||||||
ENABLE_PROFILE_UPDATE,
|
|
||||||
|
|
||||||
/** 令牌有效期 */
|
|
||||||
TOKEN_TTL
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 15:21
|
|
||||||
*/
|
|
||||||
public enum Comment {
|
|
||||||
|
|
||||||
/** 启用服务 */
|
|
||||||
ENABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 15:03
|
|
||||||
*/
|
|
||||||
public enum ICP {
|
|
||||||
|
|
||||||
IMYEYU_COM
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 15:01
|
|
||||||
*/
|
|
||||||
public enum ForeverMC {
|
|
||||||
|
|
||||||
PLAYER_BIND_MAX,
|
|
||||||
|
|
||||||
LOGIN_TOKEN_TTL
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:57
|
|
||||||
*/
|
|
||||||
public enum Multilingual {
|
|
||||||
|
|
||||||
/** 翻译 API */
|
|
||||||
TRANSLATE_API,
|
|
||||||
|
|
||||||
/** 翻译 AppId */
|
|
||||||
TRANSLATE_APP_ID,
|
|
||||||
|
|
||||||
/** 翻译密钥 */
|
|
||||||
TRANSLATE_KEY,
|
|
||||||
|
|
||||||
/** 翻译计划任务表达式 */
|
|
||||||
TRANSLATE_CORN,
|
|
||||||
|
|
||||||
/** 缓存时长 */
|
|
||||||
CACHE_TTL
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:55
|
|
||||||
*/
|
|
||||||
public enum Gitea {
|
|
||||||
|
|
||||||
/** 接口地址 */
|
|
||||||
API,
|
|
||||||
|
|
||||||
/** 关于页面所属文章 ID */
|
|
||||||
ABOUT_ARTICLE_ID,
|
|
||||||
|
|
||||||
/** 仓库文件地址 */
|
|
||||||
REPOSITORY_PATH
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:52
|
|
||||||
*/
|
|
||||||
public enum Music {
|
|
||||||
|
|
||||||
/** 启用服务 */
|
|
||||||
ENABLE,
|
|
||||||
|
|
||||||
/** 通信消息最大帧长度 */
|
|
||||||
MAX_FRAME_LENGTH,
|
|
||||||
|
|
||||||
/** 受控端 IP */
|
|
||||||
PLAYER_IP,
|
|
||||||
|
|
||||||
/** 受控端端口 */
|
|
||||||
PLAYER_PORT,
|
|
||||||
|
|
||||||
/** 控制端 IP */
|
|
||||||
CONTROLLER_IP,
|
|
||||||
|
|
||||||
/** 控制端端口 */
|
|
||||||
CONTROLLER_PORT,
|
|
||||||
|
|
||||||
/** 控制端入口地址 */
|
|
||||||
CONTROLLER_URI,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:49
|
|
||||||
*/
|
|
||||||
public enum Journal {
|
|
||||||
|
|
||||||
/** 启用服务 */
|
|
||||||
ENABLE,
|
|
||||||
|
|
||||||
/** 审核中 */
|
|
||||||
REVIEWING,
|
|
||||||
|
|
||||||
/** 访问密钥 */
|
|
||||||
KEY,
|
|
||||||
|
|
||||||
/** 微信小程序 AppId */
|
|
||||||
APP_ID,
|
|
||||||
|
|
||||||
/** 微信小程序 AppSecret */
|
|
||||||
APP_SECRET,
|
|
||||||
|
|
||||||
/** 备忘录内容 */
|
|
||||||
MEMO,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:47
|
|
||||||
*/
|
|
||||||
public enum TempFile {
|
|
||||||
|
|
||||||
/** 启用服务 */
|
|
||||||
ENABLE,
|
|
||||||
|
|
||||||
/** 最小缓存时长 */
|
|
||||||
TTL_MIN,
|
|
||||||
|
|
||||||
/** 默认缓存时长 */
|
|
||||||
TTL_DEFAULT,
|
|
||||||
|
|
||||||
/** 最大缓存时长 */
|
|
||||||
TTL_MAX,
|
|
||||||
|
|
||||||
/** 已过期文件残留时长 */
|
|
||||||
RESIDUE_TTL,
|
|
||||||
|
|
||||||
/** 每个 IP 限制有效临时文件容量 */
|
|
||||||
LIMIT_SIZE_OF_IP
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-27 14:35
|
|
||||||
*/
|
|
||||||
public enum FileSync {
|
|
||||||
|
|
||||||
ENABLE,
|
|
||||||
|
|
||||||
CRON,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:23
|
|
||||||
*/
|
|
||||||
public enum System {
|
|
||||||
|
|
||||||
/** 文件系统基准路径 */
|
|
||||||
FILE_BASE_PATH,
|
|
||||||
|
|
||||||
/** 设置缓存时长 */
|
|
||||||
SETTING_TTL,
|
|
||||||
|
|
||||||
/** 哀悼滤镜 */
|
|
||||||
ENABLE_MOURN_FILTER,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-24 14:23
|
|
||||||
*/
|
|
||||||
public enum SystemStatus {
|
|
||||||
|
|
||||||
/** 状态采集数据量 */
|
|
||||||
LIMIT,
|
|
||||||
|
|
||||||
/** 状态采集网卡 */
|
|
||||||
NETWORK_MAC,
|
|
||||||
|
|
||||||
/** 状态采集频率 */
|
|
||||||
RATE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ package com.imyeyu.api.modules.common.mapper;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.GroovyScriptVersion;
|
import com.imyeyu.api.modules.common.entity.GroovyScriptVersion;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
/// Groovy 脚本版本 Mapper
|
/// Groovy 脚本版本 Mapper
|
||||||
///
|
///
|
||||||
/// @author Codex
|
/// @author Codex
|
||||||
/// @since 2026-09-04
|
/// @since 2026-09-04
|
||||||
public interface GroovyScriptVersionMapper extends BaseMapper<GroovyScriptVersion, String> {
|
public interface GroovyScriptVersionMapper extends BaseMapper<GroovyScriptVersion, String> {
|
||||||
|
|
||||||
|
@Select("SELECT * FROM `groovy_script_version` WHERE `script_id` = #{scriptId} " + NOT_DELETE + " ORDER BY `version` DESC " + LIMIT_1)
|
||||||
|
GroovyScriptVersion selectLatest(String scriptId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ package com.imyeyu.api.modules.common.mapper;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Notify;
|
import com.imyeyu.api.modules.common.entity.Notify;
|
||||||
import com.imyeyu.api.modules.common.vo.NotifyManagementItem;
|
import com.imyeyu.api.modules.common.vo.NotifyManagementItem;
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -16,9 +15,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface NotifyMapper extends BaseMapper<Notify, String> {
|
public interface NotifyMapper extends BaseMapper<Notify, String> {
|
||||||
|
|
||||||
/// 查询业务提醒是否已创建
|
/// 原子创建业务提醒
|
||||||
@Select("SELECT EXISTS (SELECT 1 FROM `notify` WHERE `biz_type` = #{bizType} AND `biz_reminded_type` = #{bizRemindedType} AND `biz_reminded_id` = #{bizRemindedId} AND `deleted_at` IS NULL)")
|
int insertIfAbsent(Notify notify);
|
||||||
boolean existsByBizReminded(@Param("bizType") String bizType, @Param("bizRemindedType") String bizRemindedType, @Param("bizRemindedId") String bizRemindedId);
|
|
||||||
|
|
||||||
/// 分页查询管理端通知
|
/// 分页查询管理端通知
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.imyeyu.api.modules.common.mapper;
|
|||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统配置
|
* 系统配置
|
||||||
*
|
*
|
||||||
@@ -10,4 +12,6 @@ import com.imyeyu.spring.mapper.BaseMapper;
|
|||||||
* @since 2021-07-20 22:26
|
* @since 2021-07-20 22:26
|
||||||
*/
|
*/
|
||||||
public interface SettingMapper extends BaseMapper<Setting, String> {
|
public interface SettingMapper extends BaseMapper<Setting, String> {
|
||||||
|
|
||||||
|
List<Setting> selectPublicByQuery(String bizType, String bizId, List<String> codeList);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.common.service;
|
package com.imyeyu.api.modules.common.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.bean.GroovyScriptExecutionResult;
|
||||||
import com.imyeyu.api.modules.common.entity.GroovyScript;
|
import com.imyeyu.api.modules.common.entity.GroovyScript;
|
||||||
import com.imyeyu.api.modules.common.entity.GroovyScriptVersion;
|
import com.imyeyu.api.modules.common.entity.GroovyScriptVersion;
|
||||||
import com.imyeyu.api.modules.common.bean.GroovyScriptExecutionResult;
|
|
||||||
import com.imyeyu.spring.service.BaseService;
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -16,6 +16,12 @@ import java.util.Map;
|
|||||||
/// @since 2026-09-04
|
/// @since 2026-09-04
|
||||||
public interface GroovyScriptService extends BaseService<GroovyScript, String> {
|
public interface GroovyScriptService extends BaseService<GroovyScript, String> {
|
||||||
|
|
||||||
|
/// 按业务归属查询脚本列表
|
||||||
|
///
|
||||||
|
/// @param bizType 业务归属
|
||||||
|
/// @return 脚本列表
|
||||||
|
List<GroovyScript> listByBizType(GroovyScript.BizType bizType);
|
||||||
|
|
||||||
/// 按业务归属和代码查询脚本
|
/// 按业务归属和代码查询脚本
|
||||||
///
|
///
|
||||||
/// @param bizType 业务归属
|
/// @param bizType 业务归属
|
||||||
@@ -23,26 +29,14 @@ public interface GroovyScriptService extends BaseService<GroovyScript, String> {
|
|||||||
/// @return 脚本,不存在时返回 null
|
/// @return 脚本,不存在时返回 null
|
||||||
GroovyScript getByCode(GroovyScript.BizType bizType, String code);
|
GroovyScript getByCode(GroovyScript.BizType bizType, String code);
|
||||||
|
|
||||||
/// 创建脚本元数据
|
|
||||||
///
|
|
||||||
/// 新建脚本默认停用,且没有已发布版本
|
|
||||||
///
|
|
||||||
/// @param script 脚本元数据
|
|
||||||
void create(GroovyScript script);
|
|
||||||
|
|
||||||
/// 更新脚本元数据
|
|
||||||
///
|
|
||||||
/// 业务归属和代码是脚本稳定标识,创建后不可变更
|
|
||||||
///
|
|
||||||
/// @param script 脚本元数据
|
|
||||||
void update(GroovyScript script);
|
|
||||||
|
|
||||||
/// 设置脚本启用状态
|
/// 设置脚本启用状态
|
||||||
///
|
///
|
||||||
/// @param id 脚本 ID
|
/// @param id 脚本 ID
|
||||||
/// @param enabled true 为允许执行已发布版本
|
/// @param enabled true 为允许执行已发布版本
|
||||||
void setEnabled(String id, boolean enabled);
|
void setEnabled(String id, boolean enabled);
|
||||||
|
|
||||||
|
GroovyScriptVersion getLatestVersion(String scriptId);
|
||||||
|
|
||||||
/// 创建不可变的脚本版本
|
/// 创建不可变的脚本版本
|
||||||
///
|
///
|
||||||
/// @param scriptId 脚本 ID
|
/// @param scriptId 脚本 ID
|
||||||
@@ -67,11 +61,6 @@ public interface GroovyScriptService extends BaseService<GroovyScript, String> {
|
|||||||
/// @param scriptId 脚本 ID
|
/// @param scriptId 脚本 ID
|
||||||
void unpublish(String scriptId);
|
void unpublish(String scriptId);
|
||||||
|
|
||||||
/// 删除脚本
|
|
||||||
///
|
|
||||||
/// @param id 脚本 ID
|
|
||||||
void delete(String id);
|
|
||||||
|
|
||||||
/// 执行已发布且启用的脚本
|
/// 执行已发布且启用的脚本
|
||||||
///
|
///
|
||||||
/// @param bizType 业务归属
|
/// @param bizType 业务归属
|
||||||
|
|||||||
@@ -14,13 +14,10 @@ import com.imyeyu.spring.service.BaseService;
|
|||||||
*/
|
*/
|
||||||
public interface NotifyService extends BaseService<Notify, String> {
|
public interface NotifyService extends BaseService<Notify, String> {
|
||||||
|
|
||||||
/// 查询业务提醒是否已创建
|
/// 原子创建业务提醒,重复查重键不会创建通知详情
|
||||||
///
|
///
|
||||||
/// @param bizType 业务类型
|
/// @param notify 通知
|
||||||
/// @param bizRemindedType 被提醒业务类型
|
void createIfAbsent(Notify notify);
|
||||||
/// @param bizRemindedId 被提醒业务 ID
|
|
||||||
/// @return true 为已创建
|
|
||||||
boolean existsByBizReminded(String bizType, String bizRemindedType, String bizRemindedId);
|
|
||||||
|
|
||||||
/// 分页查询当前用户创建的通知
|
/// 分页查询当前用户创建的通知
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package com.imyeyu.api.modules.common.service;
|
package com.imyeyu.api.modules.common.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.bean.SettingModule;
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
import com.imyeyu.api.modules.common.bean.attachment.SettingSupport;
|
import com.imyeyu.api.modules.common.bean.attachment.SettingSupport;
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.spring.service.BaseService;
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置服务
|
* 配置服务
|
||||||
@@ -18,12 +19,22 @@ public interface SettingService extends BaseService<Setting, String> {
|
|||||||
|
|
||||||
Setting getByCode(String bizType, String bizId, String code);
|
Setting getByCode(String bizType, String bizId, String code);
|
||||||
|
|
||||||
default Setting getSystem(Enum<?> bizType) {
|
default Setting getSystem(SettingEnum setting) {
|
||||||
return getByCode(SettingModule.fromClass(bizType.getClass()).name(), TimiJava.ZERO_UUID, bizType.toString());
|
return getByCode(setting.getBizType(), TimiJava.ZERO_UUID, setting.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Setting> listByBizId(String bizType, String bizId);
|
List<Setting> listByBizId(String bizType, String bizId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询公开配置映射
|
||||||
|
*
|
||||||
|
* @param bizType 业务类型
|
||||||
|
* @param bizId 业务 ID,可为空
|
||||||
|
* @param code 配置代码列表,可为空
|
||||||
|
* @return 公开配置映射
|
||||||
|
*/
|
||||||
|
Map<String, Setting> mapPublic(String bizType, String bizId, List<String> code);
|
||||||
|
|
||||||
default List<Setting> listBySettingSupport(SettingSupport settingSupport) {
|
default List<Setting> listBySettingSupport(SettingSupport settingSupport) {
|
||||||
String bizType = settingSupport.getClass().getSimpleName().toUpperCase();
|
String bizType = settingSupport.getClass().getSimpleName().toUpperCase();
|
||||||
return listByBizId(bizType, settingSupport.getId());
|
return listByBizId(bizType, settingSupport.getId());
|
||||||
|
|||||||
+15
-9
@@ -1,9 +1,9 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.common.bean.GroovyScriptExecutionResult;
|
||||||
import com.imyeyu.api.modules.common.entity.GroovyScript;
|
import com.imyeyu.api.modules.common.entity.GroovyScript;
|
||||||
import com.imyeyu.api.modules.common.entity.GroovyScriptVersion;
|
import com.imyeyu.api.modules.common.entity.GroovyScriptVersion;
|
||||||
import com.imyeyu.api.modules.common.bean.GroovyScriptExecutionResult;
|
|
||||||
import com.imyeyu.api.modules.common.mapper.GroovyScriptMapper;
|
import com.imyeyu.api.modules.common.mapper.GroovyScriptMapper;
|
||||||
import com.imyeyu.api.modules.common.mapper.GroovyScriptVersionMapper;
|
import com.imyeyu.api.modules.common.mapper.GroovyScriptVersionMapper;
|
||||||
import com.imyeyu.api.modules.common.service.GroovyScriptService;
|
import com.imyeyu.api.modules.common.service.GroovyScriptService;
|
||||||
@@ -52,6 +52,14 @@ public class GroovyScriptServiceImplement extends AbstractEntityService<GroovySc
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroovyScript> listByBizType(GroovyScript.BizType bizType) {
|
||||||
|
TimiException.required(bizType, "未找到脚本业务归属");
|
||||||
|
GroovyScript example = new GroovyScript();
|
||||||
|
example.setBizType(bizType);
|
||||||
|
return mapper.selectAllByExample(example);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroovyScript getByCode(GroovyScript.BizType bizType, String code) {
|
public GroovyScript getByCode(GroovyScript.BizType bizType, String code) {
|
||||||
GroovyScript example = new GroovyScript();
|
GroovyScript example = new GroovyScript();
|
||||||
@@ -95,6 +103,11 @@ public class GroovyScriptServiceImplement extends AbstractEntityService<GroovySc
|
|||||||
invalidateAfterCommit(script.getId());
|
invalidateAfterCommit(script.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroovyScriptVersion getLatestVersion(String scriptId) {
|
||||||
|
return versionMapper.selectLatest(scriptId);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GroovyScriptVersion createVersion(String scriptId, String content) {
|
public GroovyScriptVersion createVersion(String scriptId, String content) {
|
||||||
@@ -104,7 +117,7 @@ public class GroovyScriptServiceImplement extends AbstractEntityService<GroovySc
|
|||||||
|
|
||||||
GroovyScriptVersion version = new GroovyScriptVersion();
|
GroovyScriptVersion version = new GroovyScriptVersion();
|
||||||
version.setScriptId(scriptId);
|
version.setScriptId(scriptId);
|
||||||
version.setVersion(nextVersion(scriptId));
|
version.setVersion(versionMapper.selectLatest(scriptId).getVersion() + 1);
|
||||||
version.setContent(content);
|
version.setContent(content);
|
||||||
version.setChecksum(checksum(content));
|
version.setChecksum(checksum(content));
|
||||||
versionMapper.insert(version);
|
versionMapper.insert(version);
|
||||||
@@ -218,13 +231,6 @@ public class GroovyScriptServiceImplement extends AbstractEntityService<GroovySc
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int nextVersion(String scriptId) {
|
|
||||||
return listVersion(scriptId).stream()
|
|
||||||
.map(GroovyScriptVersion::getVersion)
|
|
||||||
.max(Integer::compareTo)
|
|
||||||
.orElse(0) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String cacheKey(GroovyScriptVersion version) {
|
private String cacheKey(GroovyScriptVersion version) {
|
||||||
return "%s:%s".formatted(version.getId(), version.getChecksum());
|
return "%s:%s".formatted(version.getId(), version.getChecksum());
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-61
@@ -6,10 +6,9 @@ import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
|||||||
import com.imyeyu.api.modules.common.mapper.NotifyMapper;
|
import com.imyeyu.api.modules.common.mapper.NotifyMapper;
|
||||||
import com.imyeyu.api.modules.common.service.NotifyDetailService;
|
import com.imyeyu.api.modules.common.service.NotifyDetailService;
|
||||||
import com.imyeyu.api.modules.common.service.NotifyService;
|
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.common.vo.NotifyManagementItem;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
@@ -17,12 +16,13 @@ import com.imyeyu.spring.bean.Page;
|
|||||||
import com.imyeyu.spring.bean.PageResult;
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import com.imyeyu.spring.service.AbstractEntityService;
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
|
import com.imyeyu.utils.Time;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知服务实现
|
* 通知服务实现
|
||||||
@@ -30,13 +30,10 @@ import java.util.List;
|
|||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2026-05-13 14:46
|
* @since 2026-05-13 14:46
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class NotifyServiceImplement extends AbstractEntityService<Notify, String> implements NotifyService {
|
public class NotifyServiceImplement extends AbstractEntityService<Notify, String> implements NotifyService {
|
||||||
|
|
||||||
private final LoggerService loggerService;
|
|
||||||
private final UserLoginService userLoginService;
|
|
||||||
private final NotifyDetailService detailService;
|
private final NotifyDetailService detailService;
|
||||||
|
|
||||||
private final NotifyMapper mapper;
|
private final NotifyMapper mapper;
|
||||||
@@ -46,45 +43,53 @@ public class NotifyServiceImplement extends AbstractEntityService<Notify, String
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean existsByBizReminded(String bizType, String bizRemindedType, String bizRemindedId) {
|
|
||||||
return mapper.existsByBizReminded(bizType, bizRemindedType, bizRemindedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 创建通知并级联创建通知详情
|
/// 创建通知并级联创建通知详情
|
||||||
///
|
///
|
||||||
/// @param notify 通知
|
/// @param notify 通知
|
||||||
|
@OperationAudit(AuditOperation.COMMON_NOTIFY_CREATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(Notify notify) {
|
public void create(Notify notify) {
|
||||||
Logger logger = new Logger(Logger.Module.COMMON, "COMMON_NOTIFY_CREATE");
|
prepareCreate(notify);
|
||||||
try {
|
super.create(notify);
|
||||||
TimiException.required(notify, "未找到通知");
|
createDetails(notify);
|
||||||
TimiException.required(notify.getDetailList(), "未找到通知详情列表");
|
}
|
||||||
String sendFrom = userLoginService.isLogged() ? userLoginService.getRequireLoginUserId() : null;
|
|
||||||
notify.setCreatedBy(sendFrom);
|
|
||||||
super.create(notify);
|
|
||||||
|
|
||||||
for (NotifyDetail detail : notify.getDetailList()) {
|
/// 原子创建业务提醒并级联创建通知详情
|
||||||
detail.setNotifyId(notify.getId());
|
///
|
||||||
if (TimiJava.isNotEmpty(sendFrom)) {
|
/// @param notify 通知
|
||||||
detail.setSendFrom(sendFrom);
|
@OperationAudit(AuditOperation.COMMON_NOTIFY_CREATE)
|
||||||
}
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
detailService.create(detail);
|
@Override
|
||||||
}
|
public void createIfAbsent(Notify notify) {
|
||||||
logger.setLevel(Logger.Level.INFO);
|
prepareCreate(notify);
|
||||||
logger.setResult(notify.getId());
|
if (mapper.insertIfAbsent(notify) == 0) {
|
||||||
} catch (TimiException e) {
|
return;
|
||||||
logger.setLevel(Logger.Level.WARN);
|
}
|
||||||
logger.setException(e.getMessage());
|
createDetails(notify);
|
||||||
throw e;
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
logger.setLevel(Logger.Level.ERROR);
|
/// 校验并补全通知创建字段
|
||||||
logger.setException(TimiJava.serializeThrowable(e));
|
///
|
||||||
log.error("创建通知失败", e);
|
/// @param notify 通知
|
||||||
throw new TimiException(TimiCode.ERROR, "创建通知失败", e);
|
private void prepareCreate(Notify notify) {
|
||||||
} finally {
|
TimiException.required(notify, "未找到通知");
|
||||||
loggerService.create(logger);
|
TimiException.required(notify.getDetailList(), "未找到通知详情列表");
|
||||||
|
notify.setId(TimiJava.defaultIfEmpty(notify.getId(), UUID.randomUUID().toString()));
|
||||||
|
notify.setCreatedBy(TimiJava.defaultIfEmpty(notify.getCreatedBy(), TimiJava.ZERO_UUID));
|
||||||
|
notify.setCreatedAt(TimiJava.defaultIfNull(notify.getCreatedAt(), Time.now()));
|
||||||
|
notify.setUpdatedAt(null);
|
||||||
|
notify.setDeletedAt(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 创建通知详情
|
||||||
|
///
|
||||||
|
/// @param notify 通知
|
||||||
|
private void createDetails(Notify notify) {
|
||||||
|
for (NotifyDetail detail : notify.getDetailList()) {
|
||||||
|
detail.setNotifyId(notify.getId());
|
||||||
|
detail.setSendFrom(TimiJava.defaultIfEmpty(detail.getSendFrom(), TimiJava.ZERO_UUID));
|
||||||
|
detailService.create(detail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +127,8 @@ public class NotifyServiceImplement extends AbstractEntityService<Notify, String
|
|||||||
/// @param notifyId 通知 ID
|
/// @param notifyId 通知 ID
|
||||||
/// @param userId 用户 ID
|
/// @param userId 用户 ID
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@OperationAudit(AuditOperation.COMMON_NOTIFY_DELETE)
|
||||||
public void deleteManagement(String notifyId, String userId) {
|
public void deleteManagement(String notifyId, String userId) {
|
||||||
getManagement(notifyId, userId);
|
getManagement(notifyId, userId);
|
||||||
delete(notifyId);
|
delete(notifyId);
|
||||||
@@ -132,6 +139,7 @@ public class NotifyServiceImplement extends AbstractEntityService<Notify, String
|
|||||||
/// @param notifyId 通知 ID
|
/// @param notifyId 通知 ID
|
||||||
/// @param userId 用户 ID
|
/// @param userId 用户 ID
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
public void cancelManagement(String notifyId, String userId) {
|
public void cancelManagement(String notifyId, String userId) {
|
||||||
getManagement(notifyId, userId);
|
getManagement(notifyId, userId);
|
||||||
cancel(notifyId);
|
cancel(notifyId);
|
||||||
@@ -140,32 +148,17 @@ public class NotifyServiceImplement extends AbstractEntityService<Notify, String
|
|||||||
/// 删除通知并级联软删除通知详情
|
/// 删除通知并级联软删除通知详情
|
||||||
///
|
///
|
||||||
/// @param notifyId 通知 ID
|
/// @param notifyId 通知 ID
|
||||||
|
@OperationAudit(AuditOperation.COMMON_NOTIFY_DELETE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void delete(String notifyId) {
|
public void delete(String notifyId) {
|
||||||
Logger logger = new Logger(Logger.Module.COMMON, "COMMON_NOTIFY_DELETE");
|
TimiException.required(notifyId, "未找到通知 ID");
|
||||||
try {
|
TimiException.required(get(notifyId), "未找到通知");
|
||||||
TimiException.required(notifyId, "未找到通知 ID");
|
List<NotifyDetail> detailList = detailService.listByNotifyId(notifyId);
|
||||||
TimiException.required(get(notifyId), "未找到通知");
|
for (NotifyDetail detail : detailList) {
|
||||||
List<NotifyDetail> detailList = detailService.listByNotifyId(notifyId);
|
detailService.delete(detail.getId());
|
||||||
for (NotifyDetail detail : detailList) {
|
|
||||||
detailService.delete(detail.getId());
|
|
||||||
}
|
|
||||||
super.delete(notifyId);
|
|
||||||
logger.setLevel(Logger.Level.INFO);
|
|
||||||
logger.setResult(notifyId);
|
|
||||||
} 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("删除通知失败", e);
|
|
||||||
throw new TimiException(TimiCode.ERROR, "删除通知失败", e);
|
|
||||||
} finally {
|
|
||||||
loggerService.create(logger);
|
|
||||||
}
|
}
|
||||||
|
super.delete(notifyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 取消通知中尚未成功发送的通知详情
|
/// 取消通知中尚未成功发送的通知详情
|
||||||
|
|||||||
+8
@@ -17,6 +17,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统配置服务实现
|
* 系统配置服务实现
|
||||||
@@ -53,6 +55,12 @@ public class SettingServiceImplement extends AbstractEntityService<Setting, Stri
|
|||||||
return mapper.selectAllByExample(example);
|
return mapper.selectAllByExample(example);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Setting> mapPublic(String bizType, String bizId, List<String> code) {
|
||||||
|
TimiException.required(bizType, "not found bizType");
|
||||||
|
return mapper.selectPublicByQuery(bizType, bizId, code).stream().collect(Collectors.toMap(Setting::getCode, Function.identity()));
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void updateByBizId(String bizType, String bizId, List<Setting> list) {
|
public void updateByBizId(String bizType, String bizId, List<Setting> list) {
|
||||||
|
|||||||
+6
-5
@@ -3,6 +3,7 @@ package com.imyeyu.api.modules.common.service.implement;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.imyeyu.api.modules.common.bean.TempFileSetting;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
@@ -52,11 +53,11 @@ public class TempFileServiceImplement implements TempFileService {
|
|||||||
try {
|
try {
|
||||||
logger.setContent(jackson.writeValueAsString(logRequestArgs(files, ttl)));
|
logger.setContent(jackson.writeValueAsString(logRequestArgs(files, ttl)));
|
||||||
|
|
||||||
Setting ttlMin = settingService.getSystem(Setting.Module.TempFile.TTL_MIN);
|
Setting ttlMin = settingService.getSystem(TempFileSetting.TTL_MIN);
|
||||||
Setting ttlMax = settingService.getSystem(Setting.Module.TempFile.TTL_MAX);
|
Setting ttlMax = settingService.getSystem(TempFileSetting.TTL_MAX);
|
||||||
Setting ttlDefault = settingService.getSystem(Setting.Module.TempFile.TTL_DEFAULT);
|
Setting ttlDefault = settingService.getSystem(TempFileSetting.TTL_DEFAULT);
|
||||||
Setting residueTime = settingService.getSystem(Setting.Module.TempFile.RESIDUE_TTL);
|
Setting residueTime = settingService.getSystem(TempFileSetting.RESIDUE_TTL);
|
||||||
String limitStr = settingService.getSystem(Setting.Module.TempFile.LIMIT_SIZE_OF_IP).asString();
|
String limitStr = settingService.getSystem(TempFileSetting.LIMIT_SIZE_OF_IP).asString();
|
||||||
|
|
||||||
long limit = IOSize.parse(limitStr);
|
long limit = IOSize.parse(limitStr);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.imyeyu.api.modules.common.vo;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 客户端诊断日志请求
|
||||||
|
///
|
||||||
|
/// 客户端日志不是操作审计,操作编码、模块、级别、用户和 IP 均由服务端决定
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-09
|
||||||
|
@Data
|
||||||
|
public class ClientLogRequest {
|
||||||
|
|
||||||
|
/// 日志内容
|
||||||
|
@Size(max = 32768, message = "日志内容不能超过 32 KB")
|
||||||
|
private String content;
|
||||||
|
}
|
||||||
@@ -111,6 +111,8 @@ public enum GaoPermissionCode implements BuiltinPermissionCode {
|
|||||||
|
|
||||||
CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和权益配额调整", true, true),
|
CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和权益配额调整", true, true),
|
||||||
|
|
||||||
|
CUSTOMER_PACKAGE_GIFT_CREATE("CUSTOMER_PACKAGE:GIFT_CREATE", "客户套餐创建赠送", true, true),
|
||||||
|
|
||||||
FUND_RECORD_READ("FUND_RECORD:READ", "资金流水读取", true, true),
|
FUND_RECORD_READ("FUND_RECORD:READ", "资金流水读取", true, true),
|
||||||
|
|
||||||
FUND_RECEIVE_CREATE("FUND_RECEIVE:CREATE", "线下收款创建", true, true),
|
FUND_RECEIVE_CREATE("FUND_RECEIVE:CREATE", "线下收款创建", true, true),
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.annotation.SettingBizType;
|
||||||
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// @author 夜雨
|
||||||
|
/// @since 2026-09-09 16:11
|
||||||
|
@Getter
|
||||||
|
@SettingBizType("GAO_SYSTEM")
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum GaoSystemSetting implements SettingEnum {
|
||||||
|
|
||||||
|
SERVICE_LICENSE(null, Setting.ValueType.STRING),
|
||||||
|
|
||||||
|
PRIVACY_LICENSE(null, Setting.ValueType.STRING),
|
||||||
|
|
||||||
|
OPEN_SOURCE_LICENSE(null, Setting.ValueType.STRING);
|
||||||
|
|
||||||
|
final String defaultValue;
|
||||||
|
|
||||||
|
final Setting.ValueType valueType;
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoCustomerChartService;
|
import com.imyeyu.api.modules.gao.service.GaoCustomerChartService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseWordReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseWordView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
@@ -51,4 +53,15 @@ public class GaoCustomerChartController {
|
|||||||
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||||
return service.eventRecordCalendar(req);
|
return service.eventRecordCalendar(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 查询客户病症词频
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/customer/disease/word")
|
||||||
|
public List<GaoCustomerDiseaseWordView> customerDiseaseWord(@RequestBody GaoCustomerDiseaseWordReq req) {
|
||||||
|
TimiException.required(req, "not found req");
|
||||||
|
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||||
|
return service.customerDiseaseWord(req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.gao.controller;
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.entity.GroovyScript;
|
||||||
|
import com.imyeyu.api.modules.common.service.GroovyScriptService;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
@@ -31,6 +33,7 @@ public class GaoPointRuleController {
|
|||||||
|
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
private final GaoPointRuleService ruleService;
|
private final GaoPointRuleService ruleService;
|
||||||
|
private final GroovyScriptService groovyScriptService;
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@@ -54,9 +57,22 @@ public class GaoPointRuleController {
|
|||||||
GaoPointRule rule = ruleService.get(id);
|
GaoPointRule rule = ruleService.get(id);
|
||||||
TimiException.required(rule, "not found point rule");
|
TimiException.required(rule, "not found point rule");
|
||||||
storeScopeService.checkStoreId(rule.getStoreId());
|
storeScopeService.checkStoreId(rule.getStoreId());
|
||||||
|
if (rule.getRuleMode() == GaoPointRule.RuleMode.GROOVY) {
|
||||||
|
GroovyScript script = groovyScriptService.get(rule.getGroovyScriptId());
|
||||||
|
script.setLatest(groovyScriptService.getLatestVersion(script.getId()));
|
||||||
|
rule.setGroovyScript(script);
|
||||||
|
}
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_READ)
|
||||||
|
@PostMapping("/rule/script/list")
|
||||||
|
public List<GroovyScript> scriptList() {
|
||||||
|
return groovyScriptService.listByBizType(GroovyScript.BizType.GAO_POINT_RULE);
|
||||||
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_CREATE)
|
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_CREATE)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.gao.controller;
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.PermissionMatchMode;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.bean.PermissionMatchMode;
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoOrderPaymentService;
|
import com.imyeyu.api.modules.gao.service.GaoOrderPaymentService;
|
||||||
@@ -17,6 +17,8 @@ import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRevokeRequest;
|
|||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
@@ -40,6 +42,7 @@ public class GaoSalesOrderController {
|
|||||||
private final GaoSalesOrderService service;
|
private final GaoSalesOrderService service;
|
||||||
private final GaoOrderPaymentService paymentService;
|
private final GaoOrderPaymentService paymentService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@@ -56,7 +59,11 @@ public class GaoSalesOrderController {
|
|||||||
@RequireGaoPermission(GaoPermissionCode.SALES_ORDER_READ)
|
@RequireGaoPermission(GaoPermissionCode.SALES_ORDER_READ)
|
||||||
@PostMapping("/detail")
|
@PostMapping("/detail")
|
||||||
public GaoSalesOrder detail(@RequestParam String id) {
|
public GaoSalesOrder detail(@RequestParam String id) {
|
||||||
return service.detail(id);
|
GaoSalesOrder result = service.detail(id);
|
||||||
|
if (TimiJava.isNotEmpty(result.getCancelOperatorUserId())) {
|
||||||
|
result.setCancelOperator(userService.get(result.getCancelOperatorUserId()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
|||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreChartService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreChartService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerAgeStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
@@ -54,6 +55,15 @@ public class GaoStoreChartController {
|
|||||||
return service.customerGender(storeScopeService.resolveStoreId(storeId));
|
return service.customerGender(storeScopeService.resolveStoreId(storeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 查询客户年龄分布
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/customer/age")
|
||||||
|
public List<GaoCustomerAgeStatView> customerAge(@RequestParam String storeId) {
|
||||||
|
return service.customerAge(storeScopeService.resolveStoreId(storeId));
|
||||||
|
}
|
||||||
|
|
||||||
/// 查询登记事件每日统计,可同时支撑登记趋势和事件结构图表
|
/// 查询登记事件每日统计,可同时支撑登记趋势和事件结构图表
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.imyeyu.api.modules.common.entity.Attachment;
|
|||||||
import com.imyeyu.api.modules.common.entity.TagApply;
|
import com.imyeyu.api.modules.common.entity.TagApply;
|
||||||
import com.imyeyu.api.modules.user.bean.Gender;
|
import com.imyeyu.api.modules.user.bean.Gender;
|
||||||
import com.imyeyu.api.modules.user.entity.User;
|
import com.imyeyu.api.modules.user.entity.User;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.spring.annotation.table.Column;
|
import com.imyeyu.spring.annotation.table.Column;
|
||||||
import com.imyeyu.spring.annotation.table.Transient;
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
@@ -62,15 +61,12 @@ public class GaoCustomer extends UUIDEntity {
|
|||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Column("name_pinyin_full")
|
@Column("name_pinyin_full")
|
||||||
@JsonIgnore
|
|
||||||
private String namePinyinFull;
|
private String namePinyinFull;
|
||||||
|
|
||||||
@Column("name_pinyin_initial")
|
@Column("name_pinyin_initial")
|
||||||
@JsonIgnore
|
|
||||||
private String namePinyinInitial;
|
private String namePinyinInitial;
|
||||||
|
|
||||||
@Column("name_pinyin_mixed")
|
@Column("name_pinyin_mixed")
|
||||||
@JsonIgnore
|
|
||||||
private String namePinyinMixed;
|
private String namePinyinMixed;
|
||||||
|
|
||||||
/// 性别
|
/// 性别
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class GaoOrderFulfillmentItem implements IDEntity<String>, Creatable {
|
|||||||
/// SKU ID
|
/// SKU ID
|
||||||
private String skuId;
|
private String skuId;
|
||||||
|
|
||||||
/// SKU 单位 ID
|
/// 本次实际履约 SKU 单位 ID
|
||||||
private String skuUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 产品图片附件
|
/// 产品图片附件
|
||||||
@@ -45,17 +45,17 @@ public class GaoOrderFulfillmentItem implements IDEntity<String>, Creatable {
|
|||||||
/// 基础单位数量
|
/// 基础单位数量
|
||||||
private BigDecimal baseQuantity;
|
private BigDecimal baseQuantity;
|
||||||
|
|
||||||
/// 操作前待履约数量
|
/// 操作前待履约基础单位数量
|
||||||
private BigDecimal reservedBefore;
|
private BigDecimal reservedBaseBefore;
|
||||||
|
|
||||||
/// 操作后待履约数量
|
/// 操作后待履约基础单位数量
|
||||||
private BigDecimal reservedAfter;
|
private BigDecimal reservedBaseAfter;
|
||||||
|
|
||||||
/// 操作前已履约数量
|
/// 操作前已履约基础单位数量
|
||||||
private BigDecimal fulfilledBefore;
|
private BigDecimal fulfilledBaseBefore;
|
||||||
|
|
||||||
/// 操作后已履约数量
|
/// 操作后已履约基础单位数量
|
||||||
private BigDecimal fulfilledAfter;
|
private BigDecimal fulfilledBaseAfter;
|
||||||
|
|
||||||
/// 产品名称快照
|
/// 产品名称快照
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.gao.entity;
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.entity.GroovyScript;
|
||||||
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
import com.imyeyu.spring.entity.UUIDEntity;
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@@ -96,4 +98,7 @@ public class GaoPointRule extends UUIDEntity {
|
|||||||
|
|
||||||
/// 备注
|
/// 备注
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private GroovyScript groovyScript;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.gao.entity;
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.spring.annotation.table.Column;
|
||||||
import com.imyeyu.spring.annotation.table.Transient;
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
import com.imyeyu.spring.entity.UUIDEntity;
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -61,6 +63,18 @@ public class GaoProduct extends UUIDEntity {
|
|||||||
/// 产品名称
|
/// 产品名称
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@Column("name_pinyin_full")
|
||||||
|
@JsonIgnore
|
||||||
|
private String namePinyinFull;
|
||||||
|
|
||||||
|
@Column("name_pinyin_initial")
|
||||||
|
@JsonIgnore
|
||||||
|
private String namePinyinInitial;
|
||||||
|
|
||||||
|
@Column("name_pinyin_mixed")
|
||||||
|
@JsonIgnore
|
||||||
|
private String namePinyinMixed;
|
||||||
|
|
||||||
/// 产品类型
|
/// 产品类型
|
||||||
private ProductType productType;
|
private ProductType productType;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ public class GaoSalesOrderItem extends UUIDEntity {
|
|||||||
/// 订单项类型
|
/// 订单项类型
|
||||||
private ItemType itemType;
|
private ItemType itemType;
|
||||||
|
|
||||||
|
/// true 为赠送权益
|
||||||
|
private Boolean gifted;
|
||||||
|
|
||||||
/// SKU ID
|
/// SKU ID
|
||||||
private String skuId;
|
private String skuId;
|
||||||
|
|
||||||
@@ -68,14 +71,25 @@ public class GaoSalesOrderItem extends UUIDEntity {
|
|||||||
/// 基础单位数量系数快照
|
/// 基础单位数量系数快照
|
||||||
private BigDecimal baseQuantityPerUnitSnapshot;
|
private BigDecimal baseQuantityPerUnitSnapshot;
|
||||||
|
|
||||||
|
/// 当前 SKU 是否允许业务数量保留小数
|
||||||
|
@Transient
|
||||||
|
private Boolean allowDecimalQuantity;
|
||||||
|
|
||||||
|
/// 当前 SKU 基础单位名称
|
||||||
|
@Transient
|
||||||
|
private String baseUnitName;
|
||||||
|
|
||||||
/// 购买数量
|
/// 购买数量
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
|
|
||||||
/// 待履约数量
|
/// 购买基础单位数量
|
||||||
private BigDecimal reservedQuantity;
|
private BigDecimal baseQuantity;
|
||||||
|
|
||||||
/// 已履约数量
|
/// 待履约基础单位数量
|
||||||
private BigDecimal fulfilledQuantity;
|
private BigDecimal reservedBaseQuantity;
|
||||||
|
|
||||||
|
/// 已履约基础单位数量
|
||||||
|
private BigDecimal fulfilledBaseQuantity;
|
||||||
|
|
||||||
/// 实际单价,单位分
|
/// 实际单价,单位分
|
||||||
private Long unitPrice;
|
private Long unitPrice;
|
||||||
@@ -89,15 +103,15 @@ public class GaoSalesOrderItem extends UUIDEntity {
|
|||||||
/// 备注
|
/// 备注
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
/// 待核销数量
|
/// 待核销基础单位数量
|
||||||
@Transient
|
@Transient
|
||||||
private BigDecimal pendingQuantity;
|
private BigDecimal pendingBaseQuantity;
|
||||||
|
|
||||||
/// 当前可履约数量
|
/// 当前可履约基础单位数量
|
||||||
@Transient
|
@Transient
|
||||||
private BigDecimal availableQuantity;
|
private BigDecimal availableBaseQuantity;
|
||||||
|
|
||||||
/// 权益剩余数量
|
/// 权益剩余基础单位数量
|
||||||
@Transient
|
@Transient
|
||||||
private BigDecimal remainingQuantity;
|
private BigDecimal remainingBaseQuantity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.imyeyu.api.modules.gao.mapper;
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerAgeStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateCustomerView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseTextView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
@@ -27,6 +30,14 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
|
|||||||
/// @return 每日新增客户数
|
/// @return 每日新增客户数
|
||||||
List<GaoCustomerDailyStateView> stateDailyNewCustomer(@Param("storeId") String storeId, @Param("beginCreatedAt") Long beginCreatedAt, @Param("endCreatedAt") Long endCreatedAt);
|
List<GaoCustomerDailyStateView> stateDailyNewCustomer(@Param("storeId") String storeId, @Param("beginCreatedAt") Long beginCreatedAt, @Param("endCreatedAt") Long endCreatedAt);
|
||||||
|
|
||||||
|
/// 查询每日新增客户摘要
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @param beginCreatedAt 开始创建时间
|
||||||
|
/// @param endCreatedAt 结束创建时间
|
||||||
|
/// @return 每日新增客户摘要
|
||||||
|
List<GaoCustomerDailyStateCustomerView> selectDailyNewCustomer(@Param("storeId") String storeId, @Param("beginCreatedAt") Long beginCreatedAt, @Param("endCreatedAt") Long endCreatedAt);
|
||||||
|
|
||||||
/// 统计开始时间前客户总数
|
/// 统计开始时间前客户总数
|
||||||
///
|
///
|
||||||
/// @param beginCreatedAt 开始创建时间
|
/// @param beginCreatedAt 开始创建时间
|
||||||
@@ -38,6 +49,18 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
|
|||||||
/// @return 性别统计列表
|
/// @return 性别统计列表
|
||||||
List<GaoCustomerGenderStatView> stateGender(@Param("storeId") String storeId);
|
List<GaoCustomerGenderStatView> stateGender(@Param("storeId") String storeId);
|
||||||
|
|
||||||
|
/// 统计客户年龄分布
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @return 客户年龄列表
|
||||||
|
List<GaoCustomerAgeStatView> stateAge(@Param("storeId") String storeId);
|
||||||
|
|
||||||
|
/// 查询客户病症原始文本
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @return 客户备注和病症
|
||||||
|
List<GaoCustomerDiseaseTextView> selectDiseaseTextByStoreId(@Param("storeId") String storeId);
|
||||||
|
|
||||||
/// 查询客户总数
|
/// 查询客户总数
|
||||||
///
|
///
|
||||||
/// @param page 查询条件
|
/// @param page 查询条件
|
||||||
@@ -55,5 +78,5 @@ public interface GaoCustomerMapper extends BaseMapper<GaoCustomer, String> {
|
|||||||
/// @param customerId 客户 ID
|
/// @param customerId 客户 ID
|
||||||
/// @param delta 变化量
|
/// @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)
|
@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);
|
void updateInvitedCountDelta(String customerId, Long delta);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ import java.util.List;
|
|||||||
/// @since 2026-09-06
|
/// @since 2026-09-06
|
||||||
public interface GaoProductSkuMapper extends BaseMapper<GaoProductSku, String> {
|
public interface GaoProductSkuMapper extends BaseMapper<GaoProductSku, String> {
|
||||||
|
|
||||||
|
/// 批量查询 SKU
|
||||||
|
///
|
||||||
|
/// @param idList SKU ID 集合
|
||||||
|
/// @return SKU 列表
|
||||||
|
List<GaoProductSku> selectByIdList(@Param("idList") Collection<String> idList);
|
||||||
|
|
||||||
/// 按产品查询 SKU
|
/// 按产品查询 SKU
|
||||||
List<GaoProductSku> selectByProductId(@Param("productId") String productId);
|
List<GaoProductSku> selectByProductId(@Param("productId") String productId);
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ public interface GaoSalesOrderItemMapper extends BaseMapper<GaoSalesOrderItem, S
|
|||||||
/// 锁定订单明细
|
/// 锁定订单明细
|
||||||
GaoSalesOrderItem selectForUpdate(@Param("id") String id);
|
GaoSalesOrderItem selectForUpdate(@Param("id") String id);
|
||||||
|
|
||||||
/// 原子调整待履约数量
|
/// 原子调整待履约基础单位数量
|
||||||
int updateReservedQuantity(@Param("id") String id, @Param("delta") BigDecimal delta);
|
int updateReservedBaseQuantity(@Param("id") String id, @Param("delta") BigDecimal delta);
|
||||||
|
|
||||||
/// 原子调整已履约数量
|
/// 原子调整已履约基础单位数量
|
||||||
int updateFulfilledQuantity(@Param("id") String id, @Param("delta") BigDecimal delta);
|
int updateFulfilledBaseQuantity(@Param("id") String id, @Param("delta") BigDecimal delta);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.gao.service;
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseWordReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseWordView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
|
|
||||||
@@ -17,4 +19,7 @@ public interface GaoCustomerChartService {
|
|||||||
|
|
||||||
/// 查询客户登记日历记录
|
/// 查询客户登记日历记录
|
||||||
List<GaoEventRecordCalendarView> eventRecordCalendar(GaoCustomerChartReq req);
|
List<GaoEventRecordCalendarView> eventRecordCalendar(GaoCustomerChartReq req);
|
||||||
|
|
||||||
|
/// 查询客户病症词频
|
||||||
|
List<GaoCustomerDiseaseWordView> customerDiseaseWord(GaoCustomerDiseaseWordReq req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.gao.service;
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerAgeStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
||||||
@@ -87,4 +88,10 @@ public interface GaoCustomerService extends BaseService<GaoCustomer, String> {
|
|||||||
/// @param storeId 门店 ID
|
/// @param storeId 门店 ID
|
||||||
/// @return 性别统计列表
|
/// @return 性别统计列表
|
||||||
List<GaoCustomerGenderStatView> stateGender(String storeId);
|
List<GaoCustomerGenderStatView> stateGender(String storeId);
|
||||||
|
|
||||||
|
/// 统计客户年龄分布
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @return 客户年龄列表
|
||||||
|
List<GaoCustomerAgeStatView> stateAge(String storeId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ import java.util.List;
|
|||||||
/// @since 2026-09-06
|
/// @since 2026-09-06
|
||||||
public interface GaoProductSkuService extends BaseService<GaoProductSku, String> {
|
public interface GaoProductSkuService extends BaseService<GaoProductSku, String> {
|
||||||
|
|
||||||
|
/// 批量查询 SKU
|
||||||
|
///
|
||||||
|
/// @param idList SKU ID 集合
|
||||||
|
/// @return SKU 列表,包含单位、规格和附件临时数据
|
||||||
|
List<GaoProductSku> listByIdList(Collection<String> idList);
|
||||||
|
|
||||||
/// 查询 SKU 详情
|
/// 查询 SKU 详情
|
||||||
GaoProductSku detail(String id);
|
GaoProductSku detail(String id);
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ public interface GaoSalesOrderService extends BaseService<GaoSalesOrder, String>
|
|||||||
/// 检查履约权限
|
/// 检查履约权限
|
||||||
void checkFulfillmentPermission(GaoSalesOrder order);
|
void checkFulfillmentPermission(GaoSalesOrder order);
|
||||||
|
|
||||||
/// 原子调整订单项待履约数量
|
/// 原子调整订单项待履约基础单位数量
|
||||||
int updateItemReservedQuantity(String itemId, BigDecimal delta);
|
int updateItemReservedBaseQuantity(String itemId, BigDecimal delta);
|
||||||
|
|
||||||
/// 原子调整订单项已履约数量
|
/// 原子调整订单项已履约基础单位数量
|
||||||
int updateItemFulfilledQuantity(String itemId, BigDecimal delta);
|
int updateItemFulfilledBaseQuantity(String itemId, BigDecimal delta);
|
||||||
|
|
||||||
/// 锁定订单明细
|
/// 锁定订单明细
|
||||||
GaoSalesOrderItem getItemForUpdate(String itemId);
|
GaoSalesOrderItem getItemForUpdate(String itemId);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.gao.service;
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerAgeStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
@@ -21,6 +22,9 @@ public interface GaoStoreChartService {
|
|||||||
/// 查询客户性别统计
|
/// 查询客户性别统计
|
||||||
List<GaoCustomerGenderStatView> customerGender(String storeId);
|
List<GaoCustomerGenderStatView> customerGender(String storeId);
|
||||||
|
|
||||||
|
/// 查询客户年龄分布
|
||||||
|
List<GaoCustomerAgeStatView> customerAge(String storeId);
|
||||||
|
|
||||||
/// 查询登记事件每日统计
|
/// 查询登记事件每日统计
|
||||||
List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoStoreChartReq req);
|
List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoStoreChartReq req);
|
||||||
|
|
||||||
|
|||||||
+45
@@ -1,15 +1,23 @@
|
|||||||
package com.imyeyu.api.modules.gao.service.implement;
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.hankcs.hanlp.seg.common.Term;
|
||||||
|
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoCustomerMapper;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoCustomerChartService;
|
import com.imyeyu.api.modules.gao.service.GaoCustomerChartService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseTextView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseWordReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDiseaseWordView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/// 客户报表服务实现
|
/// 客户报表服务实现
|
||||||
///
|
///
|
||||||
@@ -20,6 +28,7 @@ import java.util.List;
|
|||||||
public class GaoCustomerChartServiceImplement implements GaoCustomerChartService {
|
public class GaoCustomerChartServiceImplement implements GaoCustomerChartService {
|
||||||
|
|
||||||
private final GaoEventRecordService eventRecordService;
|
private final GaoEventRecordService eventRecordService;
|
||||||
|
private final GaoCustomerMapper customerMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoCustomerChartReq req) {
|
public List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoCustomerChartReq req) {
|
||||||
@@ -33,6 +42,42 @@ public class GaoCustomerChartServiceImplement implements GaoCustomerChartService
|
|||||||
return eventRecordService.calendarByCustomerRange(req);
|
return eventRecordService.calendarByCustomerRange(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoCustomerDiseaseWordView> customerDiseaseWord(GaoCustomerDiseaseWordReq req) {
|
||||||
|
TimiException.required(req, "not found req");
|
||||||
|
TimiException.required(req.getStoreId(), "not found req.storeId");
|
||||||
|
|
||||||
|
int topN = normalizeTopN(req.getTopN());
|
||||||
|
Map<String, Long> wordCountMap = new HashMap<>();
|
||||||
|
for (GaoCustomerDiseaseTextView customer : customerMapper.selectDiseaseTextByStoreId(req.getStoreId())) {
|
||||||
|
countNounWord(customer.getDisease(), wordCountMap);
|
||||||
|
countNounWord(customer.getRemark(), wordCountMap);
|
||||||
|
}
|
||||||
|
return wordCountMap.entrySet().stream()
|
||||||
|
.sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
|
||||||
|
.limit(topN)
|
||||||
|
.map(item -> new GaoCustomerDiseaseWordView(item.getKey(), item.getValue()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void countNounWord(String text, Map<String, Long> wordCountMap) {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Term term : StandardTokenizer.segment(text)) {
|
||||||
|
if (term.nature != null && term.nature.toString().startsWith("n")) {
|
||||||
|
wordCountMap.merge(term.word, 1L, Long::sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int normalizeTopN(Integer topN) {
|
||||||
|
if (topN == null) {
|
||||||
|
return 60;
|
||||||
|
}
|
||||||
|
return Math.clamp(topN, 1, 100);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkRangeReq(GaoCustomerChartReq req) {
|
private void checkRangeReq(GaoCustomerChartReq req) {
|
||||||
TimiException.required(req, "not found req");
|
TimiException.required(req, "not found req");
|
||||||
TimiException.required(req.getStoreId(), "not found req.storeId");
|
TimiException.required(req.getStoreId(), "not found req.storeId");
|
||||||
|
|||||||
+135
-47
@@ -4,6 +4,7 @@ import com.imyeyu.api.bean.ModuleCode;
|
|||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||||
@@ -22,9 +23,10 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
|||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseItem;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseItem;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoPackageOrderCreateData;
|
import com.imyeyu.api.modules.gao.vo.GaoPackageOrderCreateData;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
@@ -38,7 +40,12 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -53,6 +60,8 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class GaoCustomerPackageServiceImplement extends AbstractEntityService<GaoCustomerPackage, String> implements GaoCustomerPackageService {
|
public class GaoCustomerPackageServiceImplement extends AbstractEntityService<GaoCustomerPackage, String> implements GaoCustomerPackageService {
|
||||||
|
|
||||||
|
private static final BigDecimal ZERO = BigDecimal.ZERO;
|
||||||
|
|
||||||
private final GaoCustomerPackageMapper mapper;
|
private final GaoCustomerPackageMapper mapper;
|
||||||
private final GaoPackageConfigService packageConfigService;
|
private final GaoPackageConfigService packageConfigService;
|
||||||
private final GaoProductSkuService skuService;
|
private final GaoProductSkuService skuService;
|
||||||
@@ -62,7 +71,6 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final PermissionChecker permissionChecker;
|
private final PermissionChecker permissionChecker;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<GaoCustomerPackage, String> mapper() {
|
protected BaseMapper<GaoCustomerPackage, String> mapper() {
|
||||||
@@ -121,10 +129,11 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
return super.get(id);
|
return super.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_CUSTOMER_PACKAGE_PURCHASE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoCustomerPackage purchase(GaoCustomerPackagePurchaseRequest request) {
|
public GaoCustomerPackage purchase(GaoCustomerPackagePurchaseRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_PURCHASE", request, () -> purchaseInternal(request));
|
return purchaseInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoCustomerPackage purchaseInternal(GaoCustomerPackagePurchaseRequest request) {
|
private GaoCustomerPackage purchaseInternal(GaoCustomerPackagePurchaseRequest request) {
|
||||||
@@ -143,9 +152,16 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
if (request.getItemList() == null) {
|
if (request.getItemList() == null) {
|
||||||
request.setItemList(defaultPurchaseItems(configItemList));
|
request.setItemList(defaultPurchaseItems(configItemList));
|
||||||
}
|
}
|
||||||
List<GaoCustomerPackagePurchaseItem> itemList = new java.util.ArrayList<>(request.getItemList());
|
List<GaoCustomerPackagePurchaseItem> requestItemList = new ArrayList<>(request.getItemList());
|
||||||
verifyPurchaseItems(itemList);
|
normalizePurchaseItems(requestItemList);
|
||||||
itemList.sort(java.util.Comparator.comparing(GaoCustomerPackagePurchaseItem::getSort));
|
verifyPurchaseItems(requestItemList);
|
||||||
|
boolean canOverridePriceAndQuantity = permissionChecker.hasAny(ModuleCode.GAO,
|
||||||
|
GaoPermissionCode.CUSTOMER_PACKAGE_PRICE_OVERRIDE.getValue());
|
||||||
|
boolean canCreateGift = permissionChecker.hasAny(ModuleCode.GAO, GaoPermissionCode.CUSTOMER_PACKAGE_GIFT_CREATE.getValue());
|
||||||
|
List<GaoCustomerPackagePurchaseItem> itemList = resolvePurchaseItems(config.getStoreId(), requestItemList, configItemList,
|
||||||
|
canOverridePriceAndQuantity, canCreateGift);
|
||||||
|
itemList.sort(Comparator.comparing(GaoCustomerPackagePurchaseItem::getSort).thenComparing(GaoCustomerPackagePurchaseItem::getSkuId)
|
||||||
|
.thenComparing(GaoCustomerPackagePurchaseItem::getSkuUnitId));
|
||||||
request.setItemList(itemList);
|
request.setItemList(itemList);
|
||||||
|
|
||||||
GaoSalesOrder existingOrder = salesOrderService.getByStoreIdAndRequestNo(config.getStoreId(), request.getRequestNo());
|
GaoSalesOrder existingOrder = salesOrderService.getByStoreIdAndRequestNo(config.getStoreId(), request.getRequestNo());
|
||||||
@@ -154,19 +170,14 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
verifyPurchaseIdempotency(existingOrder, existingPackage, request);
|
verifyPurchaseIdempotency(existingOrder, existingPackage, request);
|
||||||
return existingPackage;
|
return existingPackage;
|
||||||
}
|
}
|
||||||
verifyConfigSellableItems(config.getStoreId(), configItemList);
|
|
||||||
|
|
||||||
Long purchasedAt = TimiJava.defaultIfNull(request.getPurchasedAt(), Time.now());
|
Long purchasedAt = TimiJava.defaultIfNull(request.getPurchasedAt(), Time.now());
|
||||||
Long now = Time.now();
|
Long now = Time.now();
|
||||||
TimiException.requiredTrue(config.getStatus() == GaoPackageConfig.Status.ACTIVE, "套餐配置不可购买");
|
TimiException.requiredTrue(config.getStatus() == GaoPackageConfig.Status.ACTIVE, "套餐配置不可购买");
|
||||||
TimiException.requiredTrue(config.getBeginAt() == null || config.getBeginAt() <= now, "套餐配置尚未生效");
|
TimiException.requiredTrue(config.getBeginAt() == null || config.getBeginAt() <= now, "套餐配置尚未生效");
|
||||||
TimiException.requiredTrue(config.getEndAt() == null || now <= config.getEndAt(), "套餐配置已过期");
|
TimiException.requiredTrue(config.getEndAt() == null || now <= config.getEndAt(), "套餐配置已过期");
|
||||||
GaoCustomer customer = validateCustomer(request.getCustomerId(), config.getStoreId());
|
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) {
|
if (!canOverridePriceAndQuantity) {
|
||||||
TimiException.requiredTrue(request.getSaleAmount().equals(config.getStandardAmount()), "普通员工不能修改套餐成交价");
|
TimiException.requiredTrue(request.getSaleAmount().equals(config.getStandardAmount()), "普通员工不能修改套餐成交价");
|
||||||
verifySameQuantityAsConfig(itemList, configItemList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GaoSalesOrderItem> orderItemList = itemList.stream().map(this::toOrderItem).toList();
|
List<GaoSalesOrderItem> orderItemList = itemList.stream().map(this::toOrderItem).toList();
|
||||||
@@ -200,10 +211,11 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_CUSTOMER_PACKAGE_CANCEL)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoCustomerPackage cancel(GaoCustomerPackageCancelRequest request) {
|
public GaoCustomerPackage cancel(GaoCustomerPackageCancelRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_CANCEL", request, () -> cancelInternal(request));
|
return cancelInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoCustomerPackage cancelInternal(GaoCustomerPackageCancelRequest request) {
|
private GaoCustomerPackage cancelInternal(GaoCustomerPackageCancelRequest request) {
|
||||||
@@ -239,6 +251,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem();
|
GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem();
|
||||||
item.setSkuId(configItem.getSkuId());
|
item.setSkuId(configItem.getSkuId());
|
||||||
item.setSkuUnitId(configItem.getSkuUnitId());
|
item.setSkuUnitId(configItem.getSkuUnitId());
|
||||||
|
item.setGifted(false);
|
||||||
item.setProductNameSnapshot(configItem.getProductNameSnapshot());
|
item.setProductNameSnapshot(configItem.getProductNameSnapshot());
|
||||||
item.setProductIdSnapshot(configItem.getProductIdSnapshot());
|
item.setProductIdSnapshot(configItem.getProductIdSnapshot());
|
||||||
item.setSkuCodeSnapshot(configItem.getSkuCodeSnapshot());
|
item.setSkuCodeSnapshot(configItem.getSkuCodeSnapshot());
|
||||||
@@ -258,6 +271,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
item.setItemType(GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT);
|
item.setItemType(GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT);
|
||||||
item.setSkuId(source.getSkuId());
|
item.setSkuId(source.getSkuId());
|
||||||
item.setSkuUnitId(source.getSkuUnitId());
|
item.setSkuUnitId(source.getSkuUnitId());
|
||||||
|
item.setGifted(Boolean.TRUE.equals(source.getGifted()));
|
||||||
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||||
item.setProductIdSnapshot(source.getProductIdSnapshot());
|
item.setProductIdSnapshot(source.getProductIdSnapshot());
|
||||||
item.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
|
item.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
|
||||||
@@ -267,8 +281,9 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
item.setUnitSnapshot(source.getUnitSnapshot());
|
item.setUnitSnapshot(source.getUnitSnapshot());
|
||||||
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
||||||
item.setQuantity(source.getTotalQuantity());
|
item.setQuantity(source.getTotalQuantity());
|
||||||
item.setReservedQuantity(BigDecimal.ZERO);
|
item.setBaseQuantity(toBaseQuantity(source.getTotalQuantity(), source.getBaseQuantityPerUnitSnapshot(), "套餐权益基础数量最多保留三位小数"));
|
||||||
item.setFulfilledQuantity(BigDecimal.ZERO);
|
item.setReservedBaseQuantity(ZERO);
|
||||||
|
item.setFulfilledBaseQuantity(ZERO);
|
||||||
item.setUnitPrice(null);
|
item.setUnitPrice(null);
|
||||||
item.setLineAmount(null);
|
item.setLineAmount(null);
|
||||||
item.setSort(source.getSort());
|
item.setSort(source.getSort());
|
||||||
@@ -276,8 +291,33 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void normalizePurchaseItems(List<GaoCustomerPackagePurchaseItem> itemList) {
|
||||||
|
Map<String, Integer> indexMap = new LinkedHashMap<>();
|
||||||
|
for (int i = 0; i < itemList.size(); i++) {
|
||||||
|
GaoCustomerPackagePurchaseItem item = itemList.get(i);
|
||||||
|
if (item == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
item.setGifted(Boolean.TRUE.equals(item.getGifted()));
|
||||||
|
if (item.getSkuId() == null || item.getSkuUnitId() == null || item.getTotalQuantity() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String key = item.getSkuId() + "|" + item.getSkuUnitId() + "|" + item.getGifted();
|
||||||
|
Integer existingIndex = indexMap.putIfAbsent(key, i);
|
||||||
|
if (existingIndex == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GaoCustomerPackagePurchaseItem existingItem = itemList.get(existingIndex);
|
||||||
|
if (existingItem == null || existingItem.getTotalQuantity() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
existingItem.setTotalQuantity(existingItem.getTotalQuantity().add(item.getTotalQuantity()));
|
||||||
|
itemList.set(i, null);
|
||||||
|
}
|
||||||
|
itemList.removeIf(item -> item == null);
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyPurchaseItems(List<GaoCustomerPackagePurchaseItem> itemList) {
|
private void verifyPurchaseItems(List<GaoCustomerPackagePurchaseItem> itemList) {
|
||||||
TimiException.requiredTrue(!itemList.isEmpty(), "购买套餐至少需要一个产品");
|
|
||||||
Set<String> keySet = new HashSet<>();
|
Set<String> keySet = new HashSet<>();
|
||||||
for (int i = 0; i < itemList.size(); i++) {
|
for (int i = 0; i < itemList.size(); i++) {
|
||||||
GaoCustomerPackagePurchaseItem item = itemList.get(i);
|
GaoCustomerPackagePurchaseItem item = itemList.get(i);
|
||||||
@@ -285,46 +325,93 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
TimiException.required(item.getSkuId(), "购买 SKU 不能为空");
|
TimiException.required(item.getSkuId(), "购买 SKU 不能为空");
|
||||||
TimiException.required(item.getSkuUnitId(), "购买 SKU 单位不能为空");
|
TimiException.required(item.getSkuUnitId(), "购买 SKU 单位不能为空");
|
||||||
TimiException.required(item.getTotalQuantity(), "购买产品数量不能为空");
|
TimiException.required(item.getTotalQuantity(), "购买产品数量不能为空");
|
||||||
TimiException.requiredTrue(0 < item.getTotalQuantity().signum(), "购买产品数量必须大于 0");
|
TimiException.requiredTrue(0 <= item.getTotalQuantity().signum(), "购买产品数量不能为负数");
|
||||||
TimiException.requiredTrue(3 >= item.getTotalQuantity().stripTrailingZeros().scale(), "购买产品数量最多保留三位小数");
|
TimiException.requiredTrue(3 >= item.getTotalQuantity().stripTrailingZeros().scale(), "购买产品数量最多保留三位小数");
|
||||||
String key = item.getSkuId() + "|" + item.getSkuUnitId();
|
item.setGifted(Boolean.TRUE.equals(item.getGifted()));
|
||||||
|
String key = item.getSkuId() + "|" + item.getSkuUnitId() + "|" + item.getGifted();
|
||||||
TimiException.requiredTrue(keySet.add(key), "购买套餐内 SKU 和单位不能重复");
|
TimiException.requiredTrue(keySet.add(key), "购买套餐内 SKU 和单位不能重复");
|
||||||
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyConfigSellableItems(String storeId, List<GaoPackageConfigItem> itemList) {
|
private List<GaoCustomerPackagePurchaseItem> resolvePurchaseItems(String storeId, List<GaoCustomerPackagePurchaseItem> requestItemList,
|
||||||
for (GaoPackageConfigItem item : itemList) {
|
List<GaoPackageConfigItem> configItemList, boolean canOverridePriceAndQuantity, boolean canCreateGift) {
|
||||||
TimiException.required(item.getSkuId(), "套餐配置缺少 SKU");
|
Map<String, List<GaoPackageConfigItem>> configItemMap = configItemList.stream()
|
||||||
TimiException.required(item.getSkuUnitId(), "套餐配置缺少 SKU 单位");
|
.collect(Collectors.groupingBy(GaoPackageConfigItem::getSkuId));
|
||||||
// 通过 SKU 服务唯一选品入口重新校验 SKU 和单位仍处于可销售状态
|
Map<String, BigDecimal> configBaseQuantityMap = new HashMap<>();
|
||||||
skuService.getSellableSkuUnit(storeId, item.getSkuId(), item.getSkuUnitId());
|
for (GaoPackageConfigItem configItem : configItemList) {
|
||||||
|
configBaseQuantityMap.merge(configItem.getSkuId(),
|
||||||
|
toBaseQuantity(configItem.getDefaultQuantity(), configItem.getBaseQuantityPerUnitSnapshot(), "套餐配置基础数量最多保留三位小数"), BigDecimal::add);
|
||||||
}
|
}
|
||||||
|
Map<String, BigDecimal> requestBaseQuantityMap = new HashMap<>();
|
||||||
|
List<GaoCustomerPackagePurchaseItem> result = new ArrayList<>();
|
||||||
|
for (GaoCustomerPackagePurchaseItem requestItem : requestItemList) {
|
||||||
|
boolean gifted = Boolean.TRUE.equals(requestItem.getGifted());
|
||||||
|
List<GaoPackageConfigItem> skuConfigItemList = configItemMap.get(requestItem.getSkuId());
|
||||||
|
if (gifted) {
|
||||||
|
TimiException.requiredTrue(canCreateGift, "没有客户套餐创建赠送权限");
|
||||||
|
} else {
|
||||||
|
TimiException.requiredTrue(skuConfigItemList != null && !skuConfigItemList.isEmpty(), "购买 SKU 不属于套餐配置");
|
||||||
|
}
|
||||||
|
GaoProductSkuUnitSelection selection = skuService.getSellableSkuUnit(storeId, requestItem.getSkuId(), requestItem.getSkuUnitId());
|
||||||
|
if (!gifted) {
|
||||||
|
TimiException.requiredTrue(skuConfigItemList.stream().anyMatch(configItem -> 0 <= configItem.getBaseQuantityPerUnitSnapshot()
|
||||||
|
.compareTo(selection.getUnit().getBaseQuantityPerUnit())), "套餐权益单位不能高于配置单位");
|
||||||
|
}
|
||||||
|
BigDecimal requestBaseQuantity = toBaseQuantity(requestItem.getTotalQuantity(), selection.getUnit().getBaseQuantityPerUnit(), "套餐权益基础数量最多保留三位小数");
|
||||||
|
if (!gifted) {
|
||||||
|
requestBaseQuantityMap.merge(requestItem.getSkuId(), requestBaseQuantity, BigDecimal::add);
|
||||||
|
}
|
||||||
|
if (requestItem.getTotalQuantity().signum() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!Boolean.TRUE.equals(selection.getSku().getAllowDecimalQuantity())) {
|
||||||
|
TimiException.requiredTrue(requestItem.getTotalQuantity().stripTrailingZeros().scale() <= 0, "该 SKU 不允许小数数量");
|
||||||
|
}
|
||||||
|
GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem();
|
||||||
|
item.setSkuId(selection.getSku().getId());
|
||||||
|
item.setSkuUnitId(selection.getUnit().getId());
|
||||||
|
item.setGifted(gifted);
|
||||||
|
item.setProductNameSnapshot(selection.getProduct().getName());
|
||||||
|
item.setProductIdSnapshot(selection.getProduct().getId());
|
||||||
|
item.setSkuCodeSnapshot(selection.getSku().getCode());
|
||||||
|
item.setSkuNameSnapshot(selection.getSku().getName());
|
||||||
|
item.setSpecSummarySnapshot(selection.getSku().getSpecSummary());
|
||||||
|
item.setUnitSnapshot(selection.getUnit().getName());
|
||||||
|
item.setBaseQuantityPerUnitSnapshot(selection.getUnit().getBaseQuantityPerUnit());
|
||||||
|
item.setTotalQuantity(requestItem.getTotalQuantity());
|
||||||
|
if (gifted) {
|
||||||
|
item.setSort(requestItem.getSort());
|
||||||
|
item.setRemark(requestItem.getRemark());
|
||||||
|
} else {
|
||||||
|
GaoPackageConfigItem template = resolveItemTemplate(skuConfigItemList, selection.getUnit().getId());
|
||||||
|
item.setSort(template.getSort());
|
||||||
|
item.setRemark(template.getRemark());
|
||||||
|
}
|
||||||
|
result.add(item);
|
||||||
|
}
|
||||||
|
TimiException.requiredTrue(!result.isEmpty(), "购买套餐至少需要一个产品");
|
||||||
|
if (!canOverridePriceAndQuantity) {
|
||||||
|
for (Map.Entry<String, BigDecimal> entry : configBaseQuantityMap.entrySet()) {
|
||||||
|
TimiException.requiredTrue(entry.getValue().compareTo(requestBaseQuantityMap.getOrDefault(entry.getKey(), ZERO)) == 0,
|
||||||
|
"普通员工不能修改套餐权益配额");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifySameStructureAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
private GaoPackageConfigItem resolveItemTemplate(List<GaoPackageConfigItem> configItemList, String skuUnitId) {
|
||||||
TimiException.requiredTrue(itemList.size() == configItemList.size(), "套餐权益项目必须与套餐配置一致");
|
return configItemList.stream().filter(item -> skuUnitId.equals(item.getSkuUnitId())).findFirst()
|
||||||
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), Function.identity()));
|
.orElseGet(() -> configItemList.stream().min(Comparator.comparing(GaoPackageConfigItem::getSort)).orElseThrow());
|
||||||
for (GaoPackageConfigItem configItem : configItemList) {
|
|
||||||
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getSkuId() + "|" + configItem.getSkuUnitId());
|
|
||||||
TimiException.required(item, "套餐权益项目必须与套餐配置一致");
|
|
||||||
item.setProductNameSnapshot(configItem.getProductNameSnapshot());
|
|
||||||
item.setProductIdSnapshot(configItem.getProductIdSnapshot());
|
|
||||||
item.setSkuCodeSnapshot(configItem.getSkuCodeSnapshot());
|
|
||||||
item.setSkuNameSnapshot(configItem.getSkuNameSnapshot());
|
|
||||||
item.setSpecSummarySnapshot(configItem.getSpecSummarySnapshot());
|
|
||||||
item.setUnitSnapshot(configItem.getUnitSnapshot());
|
|
||||||
item.setBaseQuantityPerUnitSnapshot(configItem.getBaseQuantityPerUnitSnapshot());
|
|
||||||
TimiException.requiredTrue(item.getSort().equals(configItem.getSort())
|
|
||||||
&& sameText(item.getRemark(), configItem.getRemark()), "套餐权益项目的名称、单位、排序和备注不可修改");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifySameQuantityAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
private BigDecimal toBaseQuantity(BigDecimal quantity, BigDecimal baseQuantityPerUnit, String message) {
|
||||||
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), Function.identity()));
|
BigDecimal result = quantity.multiply(baseQuantityPerUnit);
|
||||||
for (GaoPackageConfigItem configItem : configItemList) {
|
TimiException.requiredTrue(3 >= result.stripTrailingZeros().scale(), message);
|
||||||
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getSkuId() + "|" + configItem.getSkuUnitId());
|
try {
|
||||||
TimiException.requiredTrue(item.getTotalQuantity().compareTo(configItem.getDefaultQuantity()) == 0, "普通员工不能修改套餐权益配额");
|
return result.setScale(3, RoundingMode.UNNECESSARY);
|
||||||
|
} catch (ArithmeticException e) {
|
||||||
|
throw new TimiException(com.imyeyu.java.bean.timi.TimiCode.ERROR, message, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,9 +427,10 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
List<GaoCustomerPackagePurchaseItem> requestItemList = TimiJava.defaultIfNull(request.getItemList(), List.of());
|
List<GaoCustomerPackagePurchaseItem> requestItemList = TimiJava.defaultIfNull(request.getItemList(), List.of());
|
||||||
verifyPurchaseItems(requestItemList);
|
verifyPurchaseItems(requestItemList);
|
||||||
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应购买请求内容不一致");
|
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应购买请求内容不一致");
|
||||||
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), Function.identity()));
|
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId() + "|"
|
||||||
|
+ Boolean.TRUE.equals(item.getGifted()), Function.identity()));
|
||||||
for (GaoCustomerPackagePurchaseItem item : requestItemList) {
|
for (GaoCustomerPackagePurchaseItem item : requestItemList) {
|
||||||
GaoSalesOrderItem existingItem = existingItemMap.get(item.getSkuId() + "|" + item.getSkuUnitId());
|
GaoSalesOrderItem existingItem = existingItemMap.get(item.getSkuId() + "|" + item.getSkuUnitId() + "|" + Boolean.TRUE.equals(item.getGifted()));
|
||||||
TimiException.required(existingItem, "requestNo 对应购买请求内容不一致");
|
TimiException.required(existingItem, "requestNo 对应购买请求内容不一致");
|
||||||
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
||||||
&& existingItem.getQuantity().compareTo(item.getTotalQuantity()) == 0
|
&& existingItem.getQuantity().compareTo(item.getTotalQuantity()) == 0
|
||||||
|
|||||||
+13
@@ -14,7 +14,9 @@ import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
|
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
|
||||||
import com.imyeyu.api.modules.gao.util.CustomerXSSFBuilder;
|
import com.imyeyu.api.modules.gao.util.CustomerXSSFBuilder;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerAgeStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateCustomerView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq;
|
||||||
@@ -410,6 +412,10 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
|||||||
for (GaoCustomerDailyStateView view : mapper.stateDailyNewCustomer(req.getStoreId(), beginCreatedAt, endCreatedAt)) {
|
for (GaoCustomerDailyStateView view : mapper.stateDailyNewCustomer(req.getStoreId(), beginCreatedAt, endCreatedAt)) {
|
||||||
newCustomerCountMap.put(view.getDateValue(), TimiJava.defaultIfNull(view.getNewCustomerCount(), 0L));
|
newCustomerCountMap.put(view.getDateValue(), TimiJava.defaultIfNull(view.getNewCustomerCount(), 0L));
|
||||||
}
|
}
|
||||||
|
Map<Integer, List<GaoCustomerDailyStateCustomerView>> customerListMap = new HashMap<>();
|
||||||
|
for (GaoCustomerDailyStateCustomerView customer : mapper.selectDailyNewCustomer(req.getStoreId(), beginCreatedAt, endCreatedAt)) {
|
||||||
|
customerListMap.computeIfAbsent(customer.getDateValue(), key -> new ArrayList<>()).add(customer);
|
||||||
|
}
|
||||||
long totalCustomerCount = TimiJava.defaultIfNull(mapper.countCreatedBefore(req.getStoreId(), beginCreatedAt), 0L);
|
long totalCustomerCount = TimiJava.defaultIfNull(mapper.countCreatedBefore(req.getStoreId(), beginCreatedAt), 0L);
|
||||||
List<GaoCustomerDailyStateView> list = new ArrayList<>();
|
List<GaoCustomerDailyStateView> list = new ArrayList<>();
|
||||||
for (LocalDate date = beginDate; !date.isAfter(endDate); date = date.plusDays(1)) {
|
for (LocalDate date = beginDate; !date.isAfter(endDate); date = date.plusDays(1)) {
|
||||||
@@ -420,6 +426,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
|||||||
view.setDateValue(dateValue);
|
view.setDateValue(dateValue);
|
||||||
view.setNewCustomerCount(newCustomerCount);
|
view.setNewCustomerCount(newCustomerCount);
|
||||||
view.setTotalCustomerCount(totalCustomerCount);
|
view.setTotalCustomerCount(totalCustomerCount);
|
||||||
|
view.setCustomerList(customerListMap.getOrDefault(dateValue, List.of()));
|
||||||
list.add(view);
|
list.add(view);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
@@ -431,6 +438,12 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
|||||||
return mapper.stateGender(storeId);
|
return mapper.stateGender(storeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoCustomerAgeStatView> stateAge(String storeId) {
|
||||||
|
TimiException.required(storeId, "not found storeId");
|
||||||
|
return mapper.stateAge(storeId);
|
||||||
|
}
|
||||||
|
|
||||||
private void changeInvitedCount(String customerId, long delta) {
|
private void changeInvitedCount(String customerId, long delta) {
|
||||||
if (TimiJava.isEmpty(customerId) || delta == 0) {
|
if (TimiJava.isEmpty(customerId) || delta == 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+124
-82
@@ -29,9 +29,10 @@ import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentItemRequest;
|
|||||||
import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentPage;
|
import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentPrepareStockRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentPrepareStockRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentRevokeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentRevokeRequest;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
@@ -46,6 +47,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -77,7 +79,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
private final RoleChecker roleChecker;
|
private final RoleChecker roleChecker;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<GaoOrderFulfillment, String> mapper() {
|
protected BaseMapper<GaoOrderFulfillment, String> mapper() {
|
||||||
@@ -145,10 +146,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
return mapper.countPendingBySalesOrderId(salesOrderId);
|
return mapper.countPendingBySalesOrderId(salesOrderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_CREATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoOrderFulfillment create(GaoOrderFulfillmentCreateRequest request) {
|
public GaoOrderFulfillment create(GaoOrderFulfillmentCreateRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CREATE", request, () -> createInternal(request));
|
return createInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoOrderFulfillment createInternal(GaoOrderFulfillmentCreateRequest request) {
|
private GaoOrderFulfillment createInternal(GaoOrderFulfillmentCreateRequest request) {
|
||||||
@@ -181,10 +183,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
return detail(fulfillment.getId());
|
return detail(fulfillment.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_PREPARE_STOCK)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoOrderFulfillment prepareStock(GaoOrderFulfillmentPrepareStockRequest request) {
|
public GaoOrderFulfillment prepareStock(GaoOrderFulfillmentPrepareStockRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_PREPARE_STOCK", request, () -> prepareStockInternal(request));
|
return prepareStockInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoOrderFulfillment prepareStockInternal(GaoOrderFulfillmentPrepareStockRequest request) {
|
private GaoOrderFulfillment prepareStockInternal(GaoOrderFulfillmentPrepareStockRequest request) {
|
||||||
@@ -194,12 +197,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
||||||
TimiException.required(snapshot, "not found fulfillment");
|
TimiException.required(snapshot, "not found fulfillment");
|
||||||
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
||||||
List<SourceEntry> sourceList = loadSources(order, itemRequests(snapshot.getId()), false);
|
List<SourceEntry> sourceList = loadPersistedSources(order, itemMapper.selectByFulfillmentId(snapshot.getId()));
|
||||||
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
||||||
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
||||||
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.WAITING_STOCK, "履约单不是等待库存状态");
|
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.WAITING_STOCK, "履约单不是等待库存状态");
|
||||||
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能准备库存");
|
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能准备库存");
|
||||||
salesOrderService.checkFulfillmentPermission(order);
|
|
||||||
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
|
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
|
||||||
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
|
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
|
||||||
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
||||||
@@ -216,10 +218,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
return detail(fulfillment.getId());
|
return detail(fulfillment.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_VERIFY)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoOrderFulfillment verify(GaoOrderFulfillmentActionRequest request) {
|
public GaoOrderFulfillment verify(GaoOrderFulfillmentActionRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_VERIFY", request, () -> verifyInternal(request));
|
return verifyInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoOrderFulfillment verifyInternal(GaoOrderFulfillmentActionRequest request) {
|
private GaoOrderFulfillment verifyInternal(GaoOrderFulfillmentActionRequest request) {
|
||||||
@@ -228,12 +231,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
||||||
TimiException.required(snapshot, "not found fulfillment");
|
TimiException.required(snapshot, "not found fulfillment");
|
||||||
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
||||||
List<SourceEntry> sourceList = loadSources(order, itemRequests(snapshot.getId()), false);
|
List<SourceEntry> sourceList = loadPersistedSources(order, itemMapper.selectByFulfillmentId(snapshot.getId()));
|
||||||
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
||||||
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
||||||
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.PENDING, "履约单不是待核销状态");
|
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.PENDING, "履约单不是待核销状态");
|
||||||
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能核销履约");
|
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能核销履约");
|
||||||
salesOrderService.checkFulfillmentPermission(order);
|
|
||||||
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
|
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
|
||||||
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
|
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
|
||||||
assertReserved(sourceList);
|
assertReserved(sourceList);
|
||||||
@@ -254,16 +256,18 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
return detail(fulfillment.getId());
|
return detail(fulfillment.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_REJECT)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoOrderFulfillment reject(GaoOrderFulfillmentActionRequest request) {
|
public GaoOrderFulfillment reject(GaoOrderFulfillmentActionRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REJECT", request, () -> releaseInternal(request, GaoOrderFulfillment.Status.REJECTED));
|
return releaseInternal(request, GaoOrderFulfillment.Status.REJECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_CANCEL)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoOrderFulfillment cancel(GaoOrderFulfillmentActionRequest request) {
|
public GaoOrderFulfillment cancel(GaoOrderFulfillmentActionRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CANCEL", request, () -> cancelInternal(request));
|
return cancelInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoOrderFulfillment cancelInternal(GaoOrderFulfillmentActionRequest request) {
|
private GaoOrderFulfillment cancelInternal(GaoOrderFulfillmentActionRequest request) {
|
||||||
@@ -283,7 +287,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
GaoOrderFulfillment snapshot = mapper.selectById(request.getFulfillmentId());
|
||||||
TimiException.required(snapshot, "not found fulfillment");
|
TimiException.required(snapshot, "not found fulfillment");
|
||||||
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
GaoSalesOrder order = salesOrderService.getForUpdate(snapshot.getSalesOrderId());
|
||||||
List<SourceEntry> sourceList = loadSources(order, itemRequests(snapshot.getId()), false);
|
List<SourceEntry> sourceList = loadPersistedSources(order, itemMapper.selectByFulfillmentId(snapshot.getId()));
|
||||||
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
GaoOrderFulfillment fulfillment = lockFulfillment(snapshot.getId(), order);
|
||||||
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
storeScopeService.checkStoreId(fulfillment.getStoreId());
|
||||||
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.WAITING_STOCK
|
TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.WAITING_STOCK
|
||||||
@@ -306,10 +310,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
return detail(fulfillment.getId());
|
return detail(fulfillment.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_REVOKE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoOrderFulfillment revoke(GaoOrderFulfillmentRevokeRequest request) {
|
public GaoOrderFulfillment revoke(GaoOrderFulfillmentRevokeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REVOKE", request, () -> revokeInternal(request));
|
return revokeInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoOrderFulfillment revokeInternal(GaoOrderFulfillmentRevokeRequest request) {
|
private GaoOrderFulfillment revokeInternal(GaoOrderFulfillmentRevokeRequest request) {
|
||||||
@@ -326,7 +331,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
}
|
}
|
||||||
GaoSalesOrder order = salesOrderService.getForUpdate(sourceSnapshot.getSalesOrderId());
|
GaoSalesOrder order = salesOrderService.getForUpdate(sourceSnapshot.getSalesOrderId());
|
||||||
List<GaoOrderFulfillmentItem> sourceItemList = itemMapper.selectByFulfillmentId(sourceSnapshot.getId());
|
List<GaoOrderFulfillmentItem> sourceItemList = itemMapper.selectByFulfillmentId(sourceSnapshot.getId());
|
||||||
List<SourceEntry> sourceList = loadSources(order, copyRequestItems(sourceItemList), false);
|
List<SourceEntry> sourceList = loadPersistedSources(order, sourceItemList);
|
||||||
GaoOrderFulfillment source = lockFulfillment(sourceSnapshot.getId(), order);
|
GaoOrderFulfillment source = lockFulfillment(sourceSnapshot.getId(), order);
|
||||||
storeScopeService.checkStoreId(source.getStoreId());
|
storeScopeService.checkStoreId(source.getStoreId());
|
||||||
TimiException.requiredTrue(source.getStatus() == GaoOrderFulfillment.Status.VERIFIED && source.getSourceFulfillmentId() == null, "只有原始已核销履约可以冲正");
|
TimiException.requiredTrue(source.getStatus() == GaoOrderFulfillment.Status.VERIFIED && source.getSourceFulfillmentId() == null, "只有原始已核销履约可以冲正");
|
||||||
@@ -356,10 +361,10 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
TimiException.required(sourceItem, "履约明细来源订单项不存在");
|
TimiException.required(sourceItem, "履约明细来源订单项不存在");
|
||||||
GaoOrderFulfillmentItem reverseItem = copyFulfillmentItem(sourceItem);
|
GaoOrderFulfillmentItem reverseItem = copyFulfillmentItem(sourceItem);
|
||||||
reverseItem.setFulfillmentId(reverse.getId());
|
reverseItem.setFulfillmentId(reverse.getId());
|
||||||
reverseItem.setReservedBefore(sourceEntry.reservedBefore());
|
reverseItem.setReservedBaseBefore(sourceEntry.reservedBaseBefore());
|
||||||
reverseItem.setReservedAfter(sourceEntry.reservedBefore());
|
reverseItem.setReservedBaseAfter(sourceEntry.reservedBaseBefore());
|
||||||
reverseItem.setFulfilledBefore(sourceEntry.fulfilledBefore());
|
reverseItem.setFulfilledBaseBefore(sourceEntry.fulfilledBaseBefore());
|
||||||
reverseItem.setFulfilledAfter(sourceEntry.fulfilledBefore().subtract(sourceEntry.quantity()));
|
reverseItem.setFulfilledBaseAfter(sourceEntry.fulfilledBaseBefore().subtract(sourceEntry.baseQuantity()));
|
||||||
itemMapper.insert(reverseItem);
|
itemMapper.insert(reverseItem);
|
||||||
}
|
}
|
||||||
if (!stockItems.isEmpty()) {
|
if (!stockItems.isEmpty()) {
|
||||||
@@ -397,28 +402,28 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
|
|
||||||
private void insertSourceReservations(GaoOrderFulfillment fulfillment, List<SourceEntry> sourceList) {
|
private void insertSourceReservations(GaoOrderFulfillment fulfillment, List<SourceEntry> sourceList) {
|
||||||
for (SourceEntry source : sourceList) {
|
for (SourceEntry source : sourceList) {
|
||||||
TimiException.requiredTrue(1 == salesOrderService.updateItemReservedQuantity(source.sourceId(), source.quantity()), "订单明细占用失败");
|
TimiException.requiredTrue(1 == salesOrderService.updateItemReservedBaseQuantity(source.sourceId(), source.baseQuantity()), "订单明细占用失败");
|
||||||
GaoOrderFulfillmentItem item = source.toFulfillmentItem(fulfillment.getId());
|
GaoOrderFulfillmentItem item = source.toFulfillmentItem(fulfillment.getId());
|
||||||
itemMapper.insert(item);
|
itemMapper.insert(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decreaseReserved(SourceEntry source) {
|
private void decreaseReserved(SourceEntry source) {
|
||||||
TimiException.requiredTrue(1 == salesOrderService.updateItemReservedQuantity(source.sourceId(), source.quantity().negate()), "订单明细占用释放失败");
|
TimiException.requiredTrue(1 == salesOrderService.updateItemReservedBaseQuantity(source.sourceId(), source.baseQuantity().negate()), "订单明细占用释放失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void increaseFulfilled(SourceEntry source) {
|
private void increaseFulfilled(SourceEntry source) {
|
||||||
TimiException.requiredTrue(1 == salesOrderService.updateItemFulfilledQuantity(source.sourceId(), source.quantity()), "订单明细履约失败");
|
TimiException.requiredTrue(1 == salesOrderService.updateItemFulfilledBaseQuantity(source.sourceId(), source.baseQuantity()), "订单明细履约失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decreaseFulfilled(SourceEntry source) {
|
private void decreaseFulfilled(SourceEntry source) {
|
||||||
TimiException.requiredTrue(0 <= source.fulfilledBefore().compareTo(source.quantity()), "已履约数量不足以冲正");
|
TimiException.requiredTrue(0 <= source.fulfilledBaseBefore().compareTo(source.baseQuantity()), "已履约数量不足以冲正");
|
||||||
TimiException.requiredTrue(1 == salesOrderService.updateItemFulfilledQuantity(source.sourceId(), source.quantity().negate()), "订单明细冲正失败");
|
TimiException.requiredTrue(1 == salesOrderService.updateItemFulfilledBaseQuantity(source.sourceId(), source.baseQuantity().negate()), "订单明细冲正失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertReserved(List<SourceEntry> sourceList) {
|
private void assertReserved(List<SourceEntry> sourceList) {
|
||||||
for (SourceEntry source : sourceList) {
|
for (SourceEntry source : sourceList) {
|
||||||
TimiException.requiredTrue(0 <= source.reservedBefore().compareTo(source.quantity()), "履约待占用数量不足");
|
TimiException.requiredTrue(0 <= source.reservedBaseBefore().compareTo(source.baseQuantity()), "履约待占用数量不足");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,6 +435,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
TimiException.required(requestItem, "履约明细不能为空");
|
TimiException.required(requestItem, "履约明细不能为空");
|
||||||
String sourceId = requiredText(requestItem.getSourceOrderItemId(), "履约明细来源订单项不能为空");
|
String sourceId = requiredText(requestItem.getSourceOrderItemId(), "履约明细来源订单项不能为空");
|
||||||
requestItem.setSourceOrderItemId(sourceId);
|
requestItem.setSourceOrderItemId(sourceId);
|
||||||
|
requestItem.setSkuUnitId(requiredText(requestItem.getSkuUnitId(), "履约 SKU 单位不能为空"));
|
||||||
TimiException.requiredTrue(sourceIdSet.add(sourceId), "同一履约单不能重复来源明细");
|
TimiException.requiredTrue(sourceIdSet.add(sourceId), "同一履约单不能重复来源明细");
|
||||||
TimiException.required(requestItem.getQuantity(), "履约数量不能为空");
|
TimiException.required(requestItem.getQuantity(), "履约数量不能为空");
|
||||||
TimiException.requiredTrue(0 < requestItem.getQuantity().signum(), "履约数量必须大于 0");
|
TimiException.requiredTrue(0 < requestItem.getQuantity().signum(), "履约数量必须大于 0");
|
||||||
@@ -444,40 +450,73 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
GaoSalesOrderItem.ItemType expectedItemType = order.getOrderType() == GaoSalesOrder.OrderType.PACKAGE
|
GaoSalesOrderItem.ItemType expectedItemType = order.getOrderType() == GaoSalesOrder.OrderType.PACKAGE
|
||||||
? GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT : GaoSalesOrderItem.ItemType.DIRECT_PRODUCT;
|
? GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT : GaoSalesOrderItem.ItemType.DIRECT_PRODUCT;
|
||||||
TimiException.requiredTrue(source.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
TimiException.requiredTrue(source.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
||||||
result.add(toSource(order, source, requestItem.getQuantity(), checkAvailable));
|
GaoProductSkuUnitSelection selection = skuService.getSellableSkuUnit(order.getStoreId(), source.getSkuId(), requestItem.getSkuUnitId());
|
||||||
|
result.add(toSource(order, source, selection.getProduct(), selection.getSku(), selection.getUnit(), requestItem.getQuantity(), checkAvailable));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceEntry toSource(GaoSalesOrder order, GaoSalesOrderItem source, BigDecimal quantity, boolean checkAvailable) {
|
private List<SourceEntry> loadPersistedSources(GaoSalesOrder order, List<GaoOrderFulfillmentItem> itemList) {
|
||||||
GaoProductSku sku = skuService.get(source.getSkuId());
|
TimiException.requiredTrue(itemList != null && !itemList.isEmpty(), "履约单没有明细");
|
||||||
TimiException.required(sku, "履约来源 SKU 不存在");
|
List<GaoOrderFulfillmentItem> sortedItemList = new ArrayList<>(itemList);
|
||||||
GaoProduct product = productService.get(sku.getProductId());
|
sortedItemList.sort(Comparator.comparing(GaoOrderFulfillmentItem::getSourceOrderItemId));
|
||||||
TimiException.required(product, "履约来源产品不存在");
|
Set<String> sourceIdSet = new HashSet<>();
|
||||||
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
|
List<SourceEntry> result = new ArrayList<>();
|
||||||
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(source.getBaseQuantityPerUnitSnapshot());
|
for (GaoOrderFulfillmentItem item : sortedItemList) {
|
||||||
GaoProductSkuUnit unit = snapshotUnit(source.getSkuUnitId(), source.getUnitSnapshot(), baseQuantityPerUnit);
|
TimiException.required(item, "履约明细不能为空");
|
||||||
JsonNode skuSpecSnapshot = source.getSkuSpecSnapshot();
|
String sourceId = requiredText(item.getSourceOrderItemId(), "履约明细来源订单项不能为空");
|
||||||
return buildSource(order, source.getId(), source.getSkuId(), source.getSkuUnitId(), source.getProductIdSnapshot(), source.getProductNameSnapshot(), source.getSkuCodeSnapshot(), source.getSkuNameSnapshot(), source.getSpecSummarySnapshot(), skuSpecSnapshot,
|
TimiException.requiredTrue(sourceIdSet.add(sourceId), "同一履约单不能重复来源明细");
|
||||||
source.getUnitSnapshot(), baseQuantityPerUnit, quantity, source.getQuantity(), source.getReservedQuantity(), source.getFulfilledQuantity(),
|
GaoSalesOrderItem source = salesOrderService.getItemForUpdate(sourceId);
|
||||||
product, sku, unit, checkAvailable);
|
validateSourceOrderItem(order, source);
|
||||||
|
GaoProductSku sku = skuService.get(source.getSkuId());
|
||||||
|
TimiException.required(sku, "履约来源 SKU 不存在");
|
||||||
|
GaoProduct product = productService.get(sku.getProductId());
|
||||||
|
TimiException.required(product, "履约来源产品不存在");
|
||||||
|
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
|
||||||
|
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(item.getBaseQuantityPerUnitSnapshot());
|
||||||
|
BigDecimal quantity = requiredPositiveQuantity(item.getQuantity(), "履约数量");
|
||||||
|
BigDecimal baseQuantity = requiredBaseQuantity(item.getBaseQuantity());
|
||||||
|
TimiException.requiredTrue(baseQuantity.compareTo(toBaseQuantity(quantity, baseQuantityPerUnit, "履约基础数量最多保留三位小数")) == 0,
|
||||||
|
"履约基础数量与单位换算快照不一致");
|
||||||
|
result.add(buildSource(order, source, item.getSkuUnitId(), item.getUnitSnapshot(), baseQuantityPerUnit, quantity, baseQuantity, product, sku, false));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceEntry buildSource(GaoSalesOrder order, String orderItemId, String skuId, String unitId, String productId,
|
private SourceEntry toSource(GaoSalesOrder order, GaoSalesOrderItem source, GaoProduct product, GaoProductSku sku,
|
||||||
String productName, String skuCode, String skuName, String specSummary, JsonNode skuSpecSnapshot, String unitName, BigDecimal baseQuantityPerUnit, BigDecimal quantity, BigDecimal totalQuantity,
|
GaoProductSkuUnit unit, BigDecimal quantity, boolean checkAvailable) {
|
||||||
BigDecimal reservedQuantity, BigDecimal fulfilledQuantity, GaoProduct product, GaoProductSku sku, GaoProductSkuUnit unit,
|
TimiException.requiredTrue(source.getSkuId().equals(sku.getId()), "履约单位不属于来源 SKU");
|
||||||
boolean checkAvailable) {
|
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
|
||||||
|
quantity = requiredPositiveQuantity(quantity, "履约数量");
|
||||||
if (!Boolean.TRUE.equals(sku.getAllowDecimalQuantity())) {
|
if (!Boolean.TRUE.equals(sku.getAllowDecimalQuantity())) {
|
||||||
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
||||||
}
|
}
|
||||||
BigDecimal reservedBefore = decimal(reservedQuantity);
|
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(unit.getBaseQuantityPerUnit());
|
||||||
BigDecimal fulfilledBefore = decimal(fulfilledQuantity);
|
BigDecimal baseQuantity = toBaseQuantity(quantity, baseQuantityPerUnit, "履约基础数量最多保留三位小数");
|
||||||
|
return buildSource(order, source, unit.getId(), unit.getName(), baseQuantityPerUnit, quantity, baseQuantity, product, sku, checkAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SourceEntry buildSource(GaoSalesOrder order, GaoSalesOrderItem source, String unitId, String unitName,
|
||||||
|
BigDecimal baseQuantityPerUnit, BigDecimal quantity, BigDecimal baseQuantity, GaoProduct product, GaoProductSku sku,
|
||||||
|
boolean checkAvailable) {
|
||||||
|
BigDecimal reservedBaseBefore = decimal(source.getReservedBaseQuantity());
|
||||||
|
BigDecimal fulfilledBaseBefore = decimal(source.getFulfilledBaseQuantity());
|
||||||
|
BigDecimal totalBaseQuantity = requiredBaseQuantity(source.getBaseQuantity());
|
||||||
if (checkAvailable) {
|
if (checkAvailable) {
|
||||||
BigDecimal available = decimal(totalQuantity).subtract(reservedBefore).subtract(fulfilledBefore);
|
BigDecimal availableBaseQuantity = totalBaseQuantity.subtract(reservedBaseBefore).subtract(fulfilledBaseBefore);
|
||||||
TimiException.requiredTrue(0 <= available.compareTo(quantity), "来源产品可履约数量不足");
|
TimiException.requiredTrue(0 <= availableBaseQuantity.compareTo(baseQuantity), "来源产品可履约数量不足");
|
||||||
}
|
}
|
||||||
return new SourceEntry(orderItemId, skuId, unitId, productId, productName, skuCode, skuName, specSummary, skuSpecSnapshot, unitName, baseQuantityPerUnit,
|
return new SourceEntry(source.getId(), source.getSkuId(), unitId, source.getProductIdSnapshot(), source.getProductNameSnapshot(),
|
||||||
quantity, decimal(totalQuantity), reservedBefore, fulfilledBefore, product, sku, unit);
|
source.getSkuCodeSnapshot(), source.getSkuNameSnapshot(), source.getSpecSummarySnapshot(), source.getSkuSpecSnapshot(), unitName,
|
||||||
|
baseQuantityPerUnit, quantity, baseQuantity, totalBaseQuantity, reservedBaseBefore, fulfilledBaseBefore, product, sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateSourceOrderItem(GaoSalesOrder order, GaoSalesOrderItem source) {
|
||||||
|
TimiException.required(source, "not found sales order item");
|
||||||
|
TimiException.requiredTrue(order.getId().equals(source.getSalesOrderId()), "订单明细不属于当前订单");
|
||||||
|
GaoSalesOrderItem.ItemType expectedItemType = order.getOrderType() == GaoSalesOrder.OrderType.PACKAGE
|
||||||
|
? GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT : GaoSalesOrderItem.ItemType.DIRECT_PRODUCT;
|
||||||
|
TimiException.requiredTrue(source.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<GaoStockChangeItem> buildStockItems(String storeId, List<SourceEntry> sourceList) {
|
private List<GaoStockChangeItem> buildStockItems(String storeId, List<SourceEntry> sourceList) {
|
||||||
@@ -489,7 +528,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
GaoProductSkuUnit baseUnit = skuService.listUnitBySkuId(source.sku().getId()).stream()
|
GaoProductSkuUnit baseUnit = skuService.listUnitBySkuId(source.sku().getId()).stream()
|
||||||
.filter(unit -> Boolean.TRUE.equals(unit.getIsBase())).findFirst().orElse(null);
|
.filter(unit -> Boolean.TRUE.equals(unit.getIsBase())).findFirst().orElse(null);
|
||||||
TimiException.required(baseUnit, "实物产品缺少基础库存单位");
|
TimiException.required(baseUnit, "实物产品缺少基础库存单位");
|
||||||
BigDecimal baseQuantity = source.quantity().multiply(source.unit().getBaseQuantityPerUnit());
|
BigDecimal baseQuantity = source.baseQuantity();
|
||||||
GaoStockChangeItem item = itemMap.get(source.sku().getId());
|
GaoStockChangeItem item = itemMap.get(source.sku().getId());
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = GaoStockChangeItem.of(source.product(), source.sku(), baseUnit, baseQuantity, baseQuantity);
|
item = GaoStockChangeItem.of(source.product(), source.sku(), baseUnit, baseQuantity, baseQuantity);
|
||||||
@@ -528,21 +567,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<GaoOrderFulfillmentItemRequest> itemRequests(String fulfillmentId) {
|
|
||||||
return copyRequestItems(itemMapper.selectByFulfillmentId(fulfillmentId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<GaoOrderFulfillmentItemRequest> copyRequestItems(List<GaoOrderFulfillmentItem> itemList) {
|
|
||||||
List<GaoOrderFulfillmentItemRequest> result = new ArrayList<>();
|
|
||||||
for (GaoOrderFulfillmentItem item : TimiJava.safeIterable(itemList)) {
|
|
||||||
GaoOrderFulfillmentItemRequest request = new GaoOrderFulfillmentItemRequest();
|
|
||||||
request.setSourceOrderItemId(item.getSourceOrderItemId());
|
|
||||||
request.setQuantity(item.getQuantity());
|
|
||||||
result.add(request);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private GaoOrderFulfillmentItem copyFulfillmentItem(GaoOrderFulfillmentItem source) {
|
private GaoOrderFulfillmentItem copyFulfillmentItem(GaoOrderFulfillmentItem source) {
|
||||||
GaoOrderFulfillmentItem result = new GaoOrderFulfillmentItem();
|
GaoOrderFulfillmentItem result = new GaoOrderFulfillmentItem();
|
||||||
result.setSkuId(source.getSkuId());
|
result.setSkuId(source.getSkuId());
|
||||||
@@ -561,20 +585,36 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoProductSkuUnit snapshotUnit(String id, String name, BigDecimal baseQuantityPerUnit) {
|
|
||||||
GaoProductSkuUnit unit = new GaoProductSkuUnit();
|
|
||||||
unit.setId(id);
|
|
||||||
unit.setName(name);
|
|
||||||
unit.setBaseQuantityPerUnit(baseQuantityPerUnit);
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BigDecimal requiredBaseQuantityPerUnit(BigDecimal value) {
|
private BigDecimal requiredBaseQuantityPerUnit(BigDecimal value) {
|
||||||
TimiException.required(value, "履约来源缺少基础单位数量系数快照");
|
TimiException.required(value, "履约来源缺少基础单位数量系数快照");
|
||||||
TimiException.requiredTrue(0 < value.signum(), "履约来源基础单位数量系数无效");
|
TimiException.requiredTrue(0 < value.signum(), "履约来源基础单位数量系数无效");
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal requiredPositiveQuantity(BigDecimal value, String fieldName) {
|
||||||
|
TimiException.required(value, fieldName + "不能为空");
|
||||||
|
TimiException.requiredTrue(0 < value.signum(), fieldName + "必须大于 0");
|
||||||
|
TimiException.requiredTrue(3 >= value.stripTrailingZeros().scale(), fieldName + "最多保留三位小数");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal requiredBaseQuantity(BigDecimal value) {
|
||||||
|
TimiException.required(value, "基础单位数量不能为空");
|
||||||
|
TimiException.requiredTrue(0 < value.signum(), "基础单位数量必须大于 0");
|
||||||
|
TimiException.requiredTrue(3 >= value.stripTrailingZeros().scale(), "基础单位数量最多保留三位小数");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal toBaseQuantity(BigDecimal quantity, BigDecimal baseQuantityPerUnit, String message) {
|
||||||
|
BigDecimal result = quantity.multiply(baseQuantityPerUnit);
|
||||||
|
TimiException.requiredTrue(3 >= result.stripTrailingZeros().scale(), message);
|
||||||
|
try {
|
||||||
|
return result.setScale(3, RoundingMode.UNNECESSARY);
|
||||||
|
} catch (ArithmeticException e) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, message, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyIdempotency(GaoOrderFulfillment existing, GaoOrderFulfillmentCreateRequest request) {
|
private void verifyIdempotency(GaoOrderFulfillment existing, GaoOrderFulfillmentCreateRequest request) {
|
||||||
TimiException.requiredTrue(existing.getSalesOrderId().equals(request.getSalesOrderId())
|
TimiException.requiredTrue(existing.getSalesOrderId().equals(request.getSalesOrderId())
|
||||||
&& (request.getDeliveredAt() == null || request.getDeliveredAt().equals(existing.getDeliveredAt()))
|
&& (request.getDeliveredAt() == null || request.getDeliveredAt().equals(existing.getDeliveredAt()))
|
||||||
@@ -582,12 +622,14 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
List<GaoOrderFulfillmentItem> existingItems = itemMapper.selectByFulfillmentId(existing.getId());
|
List<GaoOrderFulfillmentItem> existingItems = itemMapper.selectByFulfillmentId(existing.getId());
|
||||||
List<GaoOrderFulfillmentItemRequest> requestItems = request.getItemList();
|
List<GaoOrderFulfillmentItemRequest> requestItems = request.getItemList();
|
||||||
TimiException.requiredTrue(existingItems.size() == (requestItems == null ? 0 : requestItems.size()), "requestNo 对应履约请求内容不一致");
|
TimiException.requiredTrue(existingItems.size() == (requestItems == null ? 0 : requestItems.size()), "requestNo 对应履约请求内容不一致");
|
||||||
Map<String, BigDecimal> requestMap = new HashMap<>();
|
Map<String, GaoOrderFulfillmentItemRequest> requestMap = new HashMap<>();
|
||||||
for (GaoOrderFulfillmentItemRequest item : TimiJava.safeIterable(requestItems)) {
|
for (GaoOrderFulfillmentItemRequest item : TimiJava.safeIterable(requestItems)) {
|
||||||
requestMap.put(item.getSourceOrderItemId(), item.getQuantity());
|
requestMap.put(item.getSourceOrderItemId(), item);
|
||||||
}
|
}
|
||||||
for (GaoOrderFulfillmentItem item : existingItems) {
|
for (GaoOrderFulfillmentItem item : existingItems) {
|
||||||
TimiException.requiredTrue(item.getQuantity().compareTo(requestMap.getOrDefault(item.getSourceOrderItemId(), ZERO)) == 0, "requestNo 对应履约请求内容不一致");
|
GaoOrderFulfillmentItemRequest requestItem = requestMap.get(item.getSourceOrderItemId());
|
||||||
|
TimiException.requiredTrue(requestItem != null && item.getSkuUnitId().equals(requestItem.getSkuUnitId())
|
||||||
|
&& item.getQuantity().compareTo(requestItem.getQuantity()) == 0, "requestNo 对应履约请求内容不一致");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,8 +713,8 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
private record SourceEntry(String sourceOrderItemId, String skuId, String skuUnitId, String productIdSnapshot,
|
private record SourceEntry(String sourceOrderItemId, String skuId, String skuUnitId, String productIdSnapshot,
|
||||||
String productNameSnapshot, String skuCodeSnapshot, String skuNameSnapshot, String specSummarySnapshot, JsonNode skuSpecSnapshot,
|
String productNameSnapshot, String skuCodeSnapshot, String skuNameSnapshot, String specSummarySnapshot, JsonNode skuSpecSnapshot,
|
||||||
String unitSnapshot, BigDecimal baseQuantityPerUnitSnapshot, BigDecimal quantity,
|
String unitSnapshot, BigDecimal baseQuantityPerUnitSnapshot, BigDecimal quantity,
|
||||||
BigDecimal totalQuantity, BigDecimal reservedBefore, BigDecimal fulfilledBefore, GaoProduct product, GaoProductSku sku,
|
BigDecimal baseQuantity, BigDecimal totalBaseQuantity, BigDecimal reservedBaseBefore, BigDecimal fulfilledBaseBefore,
|
||||||
GaoProductSkuUnit unit) {
|
GaoProduct product, GaoProductSku sku) {
|
||||||
|
|
||||||
private String sourceId() {
|
private String sourceId() {
|
||||||
return sourceOrderItemId;
|
return sourceOrderItemId;
|
||||||
@@ -686,11 +728,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
result.setProductIdSnapshot(productIdSnapshot);
|
result.setProductIdSnapshot(productIdSnapshot);
|
||||||
result.setSourceOrderItemId(sourceOrderItemId);
|
result.setSourceOrderItemId(sourceOrderItemId);
|
||||||
result.setQuantity(quantity);
|
result.setQuantity(quantity);
|
||||||
result.setBaseQuantity(quantity.multiply(baseQuantityPerUnitSnapshot).setScale(3));
|
result.setBaseQuantity(baseQuantity);
|
||||||
result.setReservedBefore(reservedBefore);
|
result.setReservedBaseBefore(reservedBaseBefore);
|
||||||
result.setReservedAfter(reservedBefore.add(quantity));
|
result.setReservedBaseAfter(reservedBaseBefore.add(baseQuantity));
|
||||||
result.setFulfilledBefore(fulfilledBefore);
|
result.setFulfilledBaseBefore(fulfilledBaseBefore);
|
||||||
result.setFulfilledAfter(fulfilledBefore);
|
result.setFulfilledBaseAfter(fulfilledBaseBefore);
|
||||||
result.setProductNameSnapshot(productNameSnapshot);
|
result.setProductNameSnapshot(productNameSnapshot);
|
||||||
result.setSkuCodeSnapshot(skuCodeSnapshot);
|
result.setSkuCodeSnapshot(skuCodeSnapshot);
|
||||||
result.setSkuNameSnapshot(skuNameSnapshot);
|
result.setSkuNameSnapshot(skuNameSnapshot);
|
||||||
|
|||||||
+12
-12
@@ -16,8 +16,8 @@ import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentNoRefundRequest;
|
|||||||
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentReceiveRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentReceiveRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRefundRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRefundRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRevokeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRevokeRequest;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
@@ -44,12 +44,12 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final PermissionChecker permissionChecker;
|
private final PermissionChecker permissionChecker;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_PAYMENT_RECEIVE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoFundRecord receive(GaoOrderPaymentReceiveRequest request) {
|
public GaoFundRecord receive(GaoOrderPaymentReceiveRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_RECEIVE", request, () -> receiveInternal(request));
|
return receiveInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoFundRecord receiveInternal(GaoOrderPaymentReceiveRequest request) {
|
private GaoFundRecord receiveInternal(GaoOrderPaymentReceiveRequest request) {
|
||||||
@@ -80,11 +80,11 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_PAYMENT_VERIFY)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoFundRecord verify(String fundRecordId) {
|
public GaoFundRecord verify(String fundRecordId) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_VERIFY", fundRecordId, () -> {
|
GaoFundRecord record = lockOrderRecord(fundRecordId);
|
||||||
GaoFundRecord record = lockOrderRecord(fundRecordId);
|
|
||||||
TimiException.requiredTrue(record.getOperationType() == GaoFundRecord.OperationType.RECEIVE && record.getStatus() == GaoFundRecord.Status.PENDING, "收款记录不是待核销状态");
|
TimiException.requiredTrue(record.getOperationType() == GaoFundRecord.OperationType.RECEIVE && record.getStatus() == GaoFundRecord.Status.PENDING, "收款记录不是待核销状态");
|
||||||
GaoSalesOrder order = orderService.getForUpdate(record.getBizId());
|
GaoSalesOrder order = orderService.getForUpdate(record.getBizId());
|
||||||
Long paidAtBefore = order.getPaidAt();
|
Long paidAtBefore = order.getPaidAt();
|
||||||
@@ -102,7 +102,6 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
pointTriggerService.onSalesOrderPaid(order);
|
pointTriggerService.onSalesOrderPaid(order);
|
||||||
}
|
}
|
||||||
return record;
|
return record;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@@ -140,10 +139,11 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_REFUND)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoFundRecord refund(GaoOrderPaymentRefundRequest request) {
|
public GaoFundRecord refund(GaoOrderPaymentRefundRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_REFUND", request, () -> refundInternal(request));
|
return refundInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoFundRecord refundInternal(GaoOrderPaymentRefundRequest request) {
|
private GaoFundRecord refundInternal(GaoOrderPaymentRefundRequest request) {
|
||||||
@@ -177,11 +177,11 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_CONFIRM_NO_REFUND)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoFundRecord confirmNoRefund(GaoOrderPaymentNoRefundRequest request) {
|
public GaoFundRecord confirmNoRefund(GaoOrderPaymentNoRefundRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_CONFIRM_NO_REFUND", request, () -> {
|
TimiException.required(request, "not found no refund request");
|
||||||
TimiException.required(request, "not found no refund request");
|
|
||||||
GaoSalesOrder order = orderService.getForUpdate(request.getSalesOrderId());
|
GaoSalesOrder order = orderService.getForUpdate(request.getSalesOrderId());
|
||||||
String requestNo = requiredText(request.getRequestNo(), "not found requestNo");
|
String requestNo = requiredText(request.getRequestNo(), "not found requestNo");
|
||||||
String reason = requiredText(request.getReason(), "确认无需退款原因不能为空");
|
String reason = requiredText(request.getReason(), "确认无需退款原因不能为空");
|
||||||
@@ -199,13 +199,13 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
order.setRefundStatus(GaoSalesOrder.RefundStatus.REFUNDED);
|
order.setRefundStatus(GaoSalesOrder.RefundStatus.REFUNDED);
|
||||||
orderService.updatePayment(order);
|
orderService.updatePayment(order);
|
||||||
return record;
|
return record;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_FUND_REVOKE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoFundRecord revoke(GaoOrderPaymentRevokeRequest request) {
|
public GaoFundRecord revoke(GaoOrderPaymentRevokeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_FUND_REVOKE", request, () -> revokeInternal(request));
|
return revokeInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoFundRecord revokeInternal(GaoOrderPaymentRevokeRequest request) {
|
private GaoFundRecord revokeInternal(GaoOrderPaymentRevokeRequest request) {
|
||||||
|
|||||||
+3
-6
@@ -103,20 +103,17 @@ public class GaoOrderReminderServiceImplement implements GaoOrderReminderService
|
|||||||
log.warn("GAO 订单提醒跳过不存在的店长用户: storeId[{}], userId[{}]", storeId, manager.getUserId());
|
log.warn("GAO 订单提醒跳过不存在的店长用户: storeId[{}], userId[{}]", storeId, manager.getUserId());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String remindedId = "%s:%s:%s:%s".formatted(LocalDate.now(ZoneId.of(ZONE_ID)), storeId, manager.getUserId(), reminderType);
|
String remindedKey = "%s:%s:%s:%s".formatted(LocalDate.now(ZoneId.of(ZONE_ID)), storeId, manager.getUserId(), reminderType);
|
||||||
if (notifyService.existsByBizReminded(NOTIFY_BIZ_TYPE, reminderType, remindedId)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
long sendAt = Time.now();
|
long sendAt = Time.now();
|
||||||
Notify notify = new Notify();
|
Notify notify = new Notify();
|
||||||
notify.setBizType(NOTIFY_BIZ_TYPE);
|
notify.setBizType(NOTIFY_BIZ_TYPE);
|
||||||
notify.setBizRemindedType(reminderType);
|
notify.setBizRemindedType(reminderType);
|
||||||
notify.setBizRemindedId(remindedId);
|
notify.setBizRemindedKey(remindedKey);
|
||||||
notify.setDetailList(List.of(
|
notify.setDetailList(List.of(
|
||||||
createInternalDetail(manager.getUserId(), content, sendAt),
|
createInternalDetail(manager.getUserId(), content, sendAt),
|
||||||
createSmsDetail(user.getPhoneNo(), customerNames, action, content, sendAt)
|
createSmsDetail(user.getPhoneNo(), customerNames, action, content, sendAt)
|
||||||
));
|
));
|
||||||
notifyService.create(notify);
|
notifyService.createIfAbsent(notify);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("GAO 订单提醒发送失败: storeId[{}], userId[{}], reminderType[{}]", storeId, manager.getUserId(), reminderType, e);
|
log.error("GAO 订单提醒发送失败: storeId[{}], userId[{}], reminderType[{}]", storeId, manager.getUserId(), reminderType, e);
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-17
@@ -6,8 +6,8 @@ import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
|||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigItemMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigItemMapper;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigMapper;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||||
@@ -44,7 +44,6 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
|
|||||||
private final GaoPackageConfigItemMapper itemMapper;
|
private final GaoPackageConfigItemMapper itemMapper;
|
||||||
private final GaoProductSkuService skuService;
|
private final GaoProductSkuService skuService;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<GaoPackageConfig, String> mapper() {
|
protected BaseMapper<GaoPackageConfig, String> mapper() {
|
||||||
@@ -90,13 +89,11 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
|
|||||||
.collect(java.util.stream.Collectors.groupingBy(GaoPackageConfigItem::getPackageConfigId));
|
.collect(java.util.stream.Collectors.groupingBy(GaoPackageConfigItem::getPackageConfigId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PACKAGE_CONFIG_CREATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(GaoPackageConfig config) {
|
public void create(GaoPackageConfig config) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_CREATE", config, () -> {
|
createInternal(config);
|
||||||
createInternal(config);
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createInternal(GaoPackageConfig config) {
|
private void createInternal(GaoPackageConfig config) {
|
||||||
@@ -109,13 +106,11 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
|
|||||||
saveItems(config.getId(), config.getItemList());
|
saveItems(config.getId(), config.getItemList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PACKAGE_CONFIG_UPDATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void update(GaoPackageConfig request) {
|
public void update(GaoPackageConfig request) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_UPDATE", request, () -> {
|
updateInternal(request);
|
||||||
updateInternal(request);
|
|
||||||
return request;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateInternal(GaoPackageConfig request) {
|
private void updateInternal(GaoPackageConfig request) {
|
||||||
@@ -141,15 +136,13 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
|
|||||||
saveItems(dbConfig.getId(), request.getItemList());
|
saveItems(dbConfig.getId(), request.getItemList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PACKAGE_CONFIG_DELETE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void delete(String id) {
|
public void delete(String id) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_DELETE", id, () -> {
|
GaoPackageConfig config = get(id);
|
||||||
GaoPackageConfig config = get(id);
|
TimiException.required(config, "not found package config");
|
||||||
TimiException.required(config, "not found package config");
|
super.delete(id);
|
||||||
super.delete(id);
|
|
||||||
return id;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 套餐配置只允许逻辑删除
|
/// 套餐配置只允许逻辑删除
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ public class GaoPointTriggerServiceImplement implements GaoPointTriggerService {
|
|||||||
GaoPointScriptContext context = eventContext(rule, record);
|
GaoPointScriptContext context = eventContext(rule, record);
|
||||||
GroovyScriptExecutionResult<GaoPointDecision> execution = evaluate(rule, record.getStoreId(), record.getCustomerId(), record.getRegisteredAt(),
|
GroovyScriptExecutionResult<GaoPointDecision> execution = evaluate(rule, record.getStoreId(), record.getCustomerId(), record.getRegisteredAt(),
|
||||||
GaoPointRule.TriggerRefType.EVENT.name(), record.getEventId());
|
GaoPointRule.TriggerRefType.EVENT.name(), record.getEventId());
|
||||||
if (execution == null) {
|
if (execution.result() == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pointDeltaTotal += applyDecision(rule, execution, GaoPointLedger.TriggerSourceType.EVENT_RECORD, record.getId(), null,
|
pointDeltaTotal += applyDecision(rule, execution, GaoPointLedger.TriggerSourceType.EVENT_RECORD, record.getId(), null,
|
||||||
|
|||||||
+32
-40
@@ -6,8 +6,8 @@ import com.imyeyu.api.modules.gao.mapper.GaoProductCategoryMapper;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
|
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductCategoryPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductCategoryPage;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
@@ -37,7 +37,6 @@ public class GaoProductCategoryServiceImplement extends AbstractEntityService<Ga
|
|||||||
private final GaoProductCategoryMapper mapper;
|
private final GaoProductCategoryMapper mapper;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<GaoProductCategory, String> mapper() {
|
protected BaseMapper<GaoProductCategory, String> mapper() {
|
||||||
@@ -59,6 +58,7 @@ public class GaoProductCategoryServiceImplement extends AbstractEntityService<Ga
|
|||||||
return mapper.selectByStoreId(storeId);
|
return mapper.selectByStoreId(storeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_SORT)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void sort(List<String> idList) {
|
public void sort(List<String> idList) {
|
||||||
@@ -66,58 +66,50 @@ public class GaoProductCategoryServiceImplement extends AbstractEntityService<Ga
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> distinctIdList = new ArrayList<>(new LinkedHashSet<>(idList));
|
List<String> distinctIdList = new ArrayList<>(new LinkedHashSet<>(idList));
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_SORT", distinctIdList, () -> {
|
Map<String, GaoProductCategory> categoryMap = mapper.selectByIdList(new LinkedHashSet<>(distinctIdList)).stream()
|
||||||
Map<String, GaoProductCategory> categoryMap = mapper.selectByIdList(new LinkedHashSet<>(distinctIdList)).stream()
|
.collect(Collectors.toMap(GaoProductCategory::getId, Function.identity()));
|
||||||
.collect(Collectors.toMap(GaoProductCategory::getId, Function.identity()));
|
TimiException.requiredTrue(categoryMap.size() == distinctIdList.size(), "产品分类列表包含不存在的分类");
|
||||||
TimiException.requiredTrue(categoryMap.size() == distinctIdList.size(), "产品分类列表包含不存在的分类");
|
for (GaoProductCategory category : categoryMap.values()) {
|
||||||
for (GaoProductCategory category : categoryMap.values()) {
|
storeScopeService.checkStoreId(category.getStoreId());
|
||||||
storeScopeService.checkStoreId(category.getStoreId());
|
}
|
||||||
}
|
mapper.updateSort(distinctIdList, Time.now());
|
||||||
mapper.updateSort(distinctIdList, Time.now());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_CREATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(GaoProductCategory category) {
|
public void create(GaoProductCategory category) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_CREATE", category, () -> {
|
prepare(category, null);
|
||||||
prepare(category, null);
|
super.create(category);
|
||||||
super.create(category);
|
|
||||||
return category;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_UPDATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void update(GaoProductCategory request) {
|
public void update(GaoProductCategory request) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_UPDATE", request, () -> {
|
TimiException.required(request, "not found category");
|
||||||
TimiException.required(request, "not found category");
|
TimiException.required(request.getId(), "not found category.id");
|
||||||
TimiException.required(request.getId(), "not found category.id");
|
GaoProductCategory category = get(request.getId());
|
||||||
GaoProductCategory category = get(request.getId());
|
TimiException.required(category, "not found category");
|
||||||
TimiException.required(category, "not found category");
|
storeScopeService.checkStoreId(category.getStoreId());
|
||||||
storeScopeService.checkStoreId(category.getStoreId());
|
category.setName(request.getName());
|
||||||
category.setName(request.getName());
|
category.setSort(request.getSort());
|
||||||
category.setSort(request.getSort());
|
category.setStatus(request.getStatus());
|
||||||
category.setStatus(request.getStatus());
|
prepare(category, category.getId());
|
||||||
prepare(category, category.getId());
|
super.update(category);
|
||||||
super.update(category);
|
|
||||||
return category;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_DISABLE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void disable(String id) {
|
public void disable(String id) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_DISABLE", id, () -> {
|
GaoProductCategory category = get(id);
|
||||||
GaoProductCategory category = get(id);
|
TimiException.required(category, "not found category");
|
||||||
TimiException.required(category, "not found category");
|
storeScopeService.checkStoreId(category.getStoreId());
|
||||||
storeScopeService.checkStoreId(category.getStoreId());
|
category.setStatus(GaoProductCategory.Status.INACTIVE);
|
||||||
category.setStatus(GaoProductCategory.Status.INACTIVE);
|
category.setUpdatedAt(Time.now());
|
||||||
category.setUpdatedAt(Time.now());
|
category.setSort(TimiJava.defaultIfNull(category.getSort(), 0));
|
||||||
category.setSort(TimiJava.defaultIfNull(category.getSort(), 0));
|
super.update(category);
|
||||||
super.update(category);
|
|
||||||
return category;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+51
-46
@@ -14,9 +14,10 @@ import com.imyeyu.api.modules.gao.service.GaoProductService;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductSpecService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSpecService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
|
import com.imyeyu.api.util.PinyinUtil;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
@@ -52,7 +53,6 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
private final ObjectProvider<GaoProductSkuService> skuServiceProvider;
|
private final ObjectProvider<GaoProductSkuService> skuServiceProvider;
|
||||||
private final ObjectProvider<GaoProductSpecService> specServiceProvider;
|
private final ObjectProvider<GaoProductSpecService> specServiceProvider;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<GaoProduct, String> mapper() {
|
protected BaseMapper<GaoProduct, String> mapper() {
|
||||||
@@ -100,62 +100,56 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
return list.stream().filter(product -> !product.getSkuList().isEmpty()).toList();
|
return list.stream().filter(product -> !product.getSkuList().isEmpty()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_CREATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(GaoProduct product) {
|
public void create(GaoProduct product) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CREATE", product, () -> {
|
prepare(product, null);
|
||||||
prepare(product, null);
|
super.create(product);
|
||||||
super.create(product);
|
Map<String, String> specValueIdMap = saveSpecs(product, product.getSpecList());
|
||||||
Map<String, String> specValueIdMap = saveSpecs(product, product.getSpecList());
|
normalizeSkuSpecValueIds(product.getSkuList(), specValueIdMap);
|
||||||
normalizeSkuSpecValueIds(product.getSkuList(), specValueIdMap);
|
saveSkus(product, product.getSkuList());
|
||||||
saveSkus(product, product.getSkuList());
|
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList());
|
||||||
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList());
|
|
||||||
return product;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_UPDATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void update(GaoProduct request) {
|
public void update(GaoProduct request) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_UPDATE", request, () -> {
|
TimiException.required(request, "not found product");
|
||||||
TimiException.required(request, "not found product");
|
TimiException.required(request.getId(), "not found product.id");
|
||||||
TimiException.required(request.getId(), "not found product.id");
|
GaoProduct product = get(request.getId());
|
||||||
GaoProduct product = get(request.getId());
|
TimiException.required(product, "not found product");
|
||||||
TimiException.required(product, "not found product");
|
product.setCategoryId(request.getCategoryId());
|
||||||
product.setCategoryId(request.getCategoryId());
|
product.setCode(TimiJava.defaultIfNull(request.getCode(), product.getCode()));
|
||||||
product.setCode(TimiJava.defaultIfNull(request.getCode(), product.getCode()));
|
product.setName(TimiJava.defaultIfNull(request.getName(), product.getName()));
|
||||||
product.setName(TimiJava.defaultIfNull(request.getName(), product.getName()));
|
product.setProductType(TimiJava.defaultIfNull(request.getProductType(), product.getProductType()));
|
||||||
product.setProductType(TimiJava.defaultIfNull(request.getProductType(), product.getProductType()));
|
product.setStockEnabled(TimiJava.defaultIfNull(request.getStockEnabled(), product.getStockEnabled()));
|
||||||
product.setStockEnabled(TimiJava.defaultIfNull(request.getStockEnabled(), product.getStockEnabled()));
|
product.setStatus(TimiJava.defaultIfNull(request.getStatus(), product.getStatus()));
|
||||||
product.setStatus(TimiJava.defaultIfNull(request.getStatus(), product.getStatus()));
|
product.setSort(TimiJava.defaultIfNull(request.getSort(), product.getSort()));
|
||||||
product.setSort(TimiJava.defaultIfNull(request.getSort(), product.getSort()));
|
product.setRemark(request.getRemark());
|
||||||
product.setRemark(request.getRemark());
|
product.setSkuList(request.getSkuList() == null ? skuServiceProvider.getObject().listByProductId(product.getId()) : request.getSkuList());
|
||||||
product.setSkuList(request.getSkuList() == null ? skuServiceProvider.getObject().listByProductId(product.getId()) : request.getSkuList());
|
prepare(product, product.getId());
|
||||||
prepare(product, product.getId());
|
super.update(product);
|
||||||
super.update(product);
|
if (request.getSkuList() != null) {
|
||||||
if (request.getSkuList() != null) {
|
saveSkus(product, request.getSkuList());
|
||||||
saveSkus(product, request.getSkuList());
|
}
|
||||||
}
|
if (request.getAttachmentList() != null) {
|
||||||
if (request.getAttachmentList() != null) {
|
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), request.getAttachmentList());
|
||||||
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), request.getAttachmentList());
|
}
|
||||||
}
|
|
||||||
return product;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_DISABLE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void disable(String id) {
|
public void disable(String id) {
|
||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_DISABLE", id, () -> {
|
GaoProduct product = get(id);
|
||||||
GaoProduct product = get(id);
|
TimiException.required(product, "not found product");
|
||||||
TimiException.required(product, "not found product");
|
TimiException.requiredTrue(skuServiceProvider.getObject().listByProductId(id).stream().noneMatch(sku -> sku.getStatus() == com.imyeyu.api.modules.gao.entity.GaoProductSku.Status.ACTIVE), "请先停用产品的全部 SKU");
|
||||||
TimiException.requiredTrue(skuServiceProvider.getObject().listByProductId(id).stream().noneMatch(sku -> sku.getStatus() == com.imyeyu.api.modules.gao.entity.GaoProductSku.Status.ACTIVE), "请先停用产品的全部 SKU");
|
product.setStatus(GaoProduct.Status.INACTIVE);
|
||||||
product.setStatus(GaoProduct.Status.INACTIVE);
|
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||||
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
product.setUpdatedAt(Time.now());
|
||||||
product.setUpdatedAt(Time.now());
|
super.update(product);
|
||||||
super.update(product);
|
|
||||||
return product;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -173,6 +167,7 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
TimiException.required(product.getStoreId(), "not found product.storeId");
|
TimiException.required(product.getStoreId(), "not found product.storeId");
|
||||||
product.setCode(requiredText(product.getCode(), "产品编码不能为空"));
|
product.setCode(requiredText(product.getCode(), "产品编码不能为空"));
|
||||||
product.setName(requiredText(product.getName(), "产品名称不能为空"));
|
product.setName(requiredText(product.getName(), "产品名称不能为空"));
|
||||||
|
fillPinyin(product);
|
||||||
product.setProductType(TimiJava.defaultIfNull(product.getProductType(), GaoProduct.ProductType.GOODS));
|
product.setProductType(TimiJava.defaultIfNull(product.getProductType(), GaoProduct.ProductType.GOODS));
|
||||||
product.setStockEnabled(TimiJava.defaultIfNull(product.getStockEnabled(), product.getProductType() != GaoProduct.ProductType.SERVICE));
|
product.setStockEnabled(TimiJava.defaultIfNull(product.getStockEnabled(), product.getProductType() != GaoProduct.ProductType.SERVICE));
|
||||||
TimiException.requiredTrue(product.getProductType() != GaoProduct.ProductType.SERVICE || !product.getStockEnabled(), "SERVICE 产品不能启用库存");
|
TimiException.requiredTrue(product.getProductType() != GaoProduct.ProductType.SERVICE || !product.getStockEnabled(), "SERVICE 产品不能启用库存");
|
||||||
@@ -268,4 +263,14 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
TimiException.requiredTrue(!result.isEmpty(), message);
|
TimiException.requiredTrue(!result.isEmpty(), message);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 填充产品名称拼音搜索字段
|
||||||
|
///
|
||||||
|
/// @param product 产品
|
||||||
|
private void fillPinyin(GaoProduct product) {
|
||||||
|
String name = product.getName();
|
||||||
|
product.setNamePinyinFull(PinyinUtil.full(name));
|
||||||
|
product.setNamePinyinInitial(PinyinUtil.initial(name));
|
||||||
|
product.setNamePinyinMixed(PinyinUtil.mixed(name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -76,6 +76,16 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService<GaoProd
|
|||||||
return sku;
|
return sku;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSku> listByIdList(Collection<String> idList) {
|
||||||
|
if (idList == null || idList.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<GaoProductSku> result = mapper.selectByIdList(idList);
|
||||||
|
fillTransient(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GaoProductSku> listByProductId(String productId) {
|
public List<GaoProductSku> listByProductId(String productId) {
|
||||||
TimiException.required(productId, "not found productId");
|
TimiException.required(productId, "not found productId");
|
||||||
|
|||||||
+21
-16
@@ -4,24 +4,24 @@ import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
|||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
|
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockProductView;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockProductView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
@@ -59,7 +59,6 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
private final GaoProductSkuService skuService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<GaoProductStock> pageByQuery(GaoProductStockPage page) {
|
public PageResult<GaoProductStock> pageByQuery(GaoProductStockPage page) {
|
||||||
@@ -112,40 +111,46 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
return stock;
|
return stock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_INITIAL_IN)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStock initialIn(GaoProductStockChangeRequest request) {
|
public GaoProductStock initialIn(GaoProductStockChangeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_INITIAL_IN", request, () -> change(request, GaoProductStockRecord.OperationType.INITIAL_IN));
|
return change(request, GaoProductStockRecord.OperationType.INITIAL_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_MANUAL_IN)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStock manualIn(GaoProductStockChangeRequest request) {
|
public GaoProductStock manualIn(GaoProductStockChangeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_IN", request, () -> change(request, GaoProductStockRecord.OperationType.MANUAL_IN));
|
return change(request, GaoProductStockRecord.OperationType.MANUAL_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_MANUAL_OUT)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStock manualOut(GaoProductStockChangeRequest request) {
|
public GaoProductStock manualOut(GaoProductStockChangeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_OUT", request, () -> change(request, GaoProductStockRecord.OperationType.MANUAL_OUT));
|
return change(request, GaoProductStockRecord.OperationType.MANUAL_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_MANUAL_REVOKE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStock manualRevoke(GaoProductStockManualRevokeRequest request) {
|
public GaoProductStock manualRevoke(GaoProductStockManualRevokeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_REVOKE", request, () -> manualRevokeInternal(request));
|
return manualRevokeInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_STOCKTAKE_GAIN)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStock stocktakeGain(GaoProductStockChangeRequest request) {
|
public GaoProductStock stocktakeGain(GaoProductStockChangeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_GAIN", request, () -> change(request, GaoProductStockRecord.OperationType.STOCKTAKE_GAIN));
|
return change(request, GaoProductStockRecord.OperationType.STOCKTAKE_GAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_STOCKTAKE_LOSS)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStock stocktakeLoss(GaoProductStockChangeRequest request) {
|
public GaoProductStock stocktakeLoss(GaoProductStockChangeRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_LOSS", request, () -> change(request, GaoProductStockRecord.OperationType.STOCKTAKE_LOSS));
|
return change(request, GaoProductStockRecord.OperationType.STOCKTAKE_LOSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -216,7 +221,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
GaoStockChangeItem item = GaoStockChangeItem.of(product, sku, unit, quantity, baseQuantity);
|
GaoStockChangeItem item = GaoStockChangeItem.of(product, sku, unit, quantity, baseQuantity);
|
||||||
item.setSkuSpecSnapshot(skuService.getSkuSpecSnapshot(sku));
|
item.setSkuSpecSnapshot(skuService.getSkuSpecSnapshot(sku));
|
||||||
List<GaoProductStock> result = executeInternal(storeId, sku.getId(), List.of(item), requestNo,
|
List<GaoProductStock> result = executeInternal(storeId, sku.getId(), List.of(item), requestNo,
|
||||||
userLoginService.getRequireLoginUserId(), operationType, request.getRemark(), request.getAttachmentList());
|
userLoginService.getRequireLoginUserId(), operationType, request.getOccurredAt(), request.getRemark(), request.getAttachmentList());
|
||||||
return result.getFirst();
|
return result.getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,11 +300,11 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
|
|
||||||
private List<GaoProductStock> executeInternal(String storeId, String bizId, List<GaoStockChangeItem> sourceList, String requestNo,
|
private List<GaoProductStock> executeInternal(String storeId, String bizId, List<GaoStockChangeItem> sourceList, String requestNo,
|
||||||
String operatorUserId, GaoProductStockRecord.OperationType operationType, String remark) {
|
String operatorUserId, GaoProductStockRecord.OperationType operationType, String remark) {
|
||||||
return executeInternal(storeId, bizId, sourceList, requestNo, operatorUserId, operationType, remark, null);
|
return executeInternal(storeId, bizId, sourceList, requestNo, operatorUserId, operationType, null, remark, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<GaoProductStock> executeInternal(String storeId, String bizId, List<GaoStockChangeItem> sourceList, String requestNo,
|
private List<GaoProductStock> executeInternal(String storeId, String bizId, List<GaoStockChangeItem> sourceList, String requestNo,
|
||||||
String operatorUserId, GaoProductStockRecord.OperationType operationType, String remark, List<Attachment> attachmentList) {
|
String operatorUserId, GaoProductStockRecord.OperationType operationType, Long occurredAt, String remark, List<Attachment> attachmentList) {
|
||||||
TimiException.required(storeId, "not found storeId");
|
TimiException.required(storeId, "not found storeId");
|
||||||
TimiException.required(bizId, "not found stock bizId");
|
TimiException.required(bizId, "not found stock bizId");
|
||||||
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "库存变更至少需要一个产品");
|
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "库存变更至少需要一个产品");
|
||||||
@@ -360,7 +365,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
record.setSourceRecordId(item.getSourceRecordId());
|
record.setSourceRecordId(item.getSourceRecordId());
|
||||||
record.setRequestNo(normalizedRequestNo);
|
record.setRequestNo(normalizedRequestNo);
|
||||||
record.setOperatorUserId(operatorUserId);
|
record.setOperatorUserId(operatorUserId);
|
||||||
record.setOccurredAt(Time.now());
|
record.setOccurredAt(TimiJava.defaultIfNull(occurredAt, Time.now()));
|
||||||
record.setRemark(remark);
|
record.setRemark(remark);
|
||||||
recordService.createOperationRecord(record, attachmentList);
|
recordService.createOperationRecord(record, attachmentList);
|
||||||
stock.setOnHandQuantity(onHandAfter);
|
stock.setOnHandQuantity(onHandAfter);
|
||||||
|
|||||||
+99
-34
@@ -34,8 +34,8 @@ import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
|
|||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderItemRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderItemRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.audit.AuditOperation;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.audit.OperationAudit;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
||||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
@@ -73,6 +73,9 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
|
|
||||||
private static final BigDecimal ZERO = BigDecimal.ZERO;
|
private static final BigDecimal ZERO = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
// 过渡兼容:旧事件登记系统已收款,但新订单可能仍为零有效收款,暂时允许店长发起履约;过渡结束改为 false 并删除此分支
|
||||||
|
private static final boolean ALLOW_STORE_MANAGER_ZERO_PAYMENT_FULFILLMENT = true;
|
||||||
|
|
||||||
private final GaoSalesOrderMapper mapper;
|
private final GaoSalesOrderMapper mapper;
|
||||||
private final GaoSalesOrderItemMapper itemMapper;
|
private final GaoSalesOrderItemMapper itemMapper;
|
||||||
private final GaoFundRecordService fundRecordService;
|
private final GaoFundRecordService fundRecordService;
|
||||||
@@ -88,7 +91,6 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
private final PermissionChecker permissionChecker;
|
private final PermissionChecker permissionChecker;
|
||||||
private final RoleChecker roleChecker;
|
private final RoleChecker roleChecker;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<GaoSalesOrder, String> mapper() {
|
protected BaseMapper<GaoSalesOrder, String> mapper() {
|
||||||
@@ -113,6 +115,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
TimiException.required(order, "not found sales order");
|
TimiException.required(order, "not found sales order");
|
||||||
storeScopeService.checkStoreId(order.getStoreId());
|
storeScopeService.checkStoreId(order.getStoreId());
|
||||||
List<GaoSalesOrderItem> itemList = itemMapper.selectBySalesOrderId(order.getId());
|
List<GaoSalesOrderItem> itemList = itemMapper.selectBySalesOrderId(order.getId());
|
||||||
|
fillItemDisplayInfo(itemList);
|
||||||
for (GaoSalesOrderItem item : itemList) {
|
for (GaoSalesOrderItem item : itemList) {
|
||||||
fillItemBalance(item);
|
fillItemBalance(item);
|
||||||
}
|
}
|
||||||
@@ -136,23 +139,23 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoSalesOrder createDirectProductOrder(GaoDirectProductOrderCreateRequest request) {
|
public GaoSalesOrder createDirectProductOrder(GaoDirectProductOrderCreateRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE", request, () -> createDirectInternal(request));
|
return createDirectInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_UPDATE)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoSalesOrder updateRemarkAndAttachments(GaoSalesOrderUpdateRequest request) {
|
public GaoSalesOrder updateRemarkAndAttachments(GaoSalesOrderUpdateRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_UPDATE", request, () -> {
|
TimiException.required(request, "not found sales order update request");
|
||||||
TimiException.required(request, "not found sales order update request");
|
TimiException.required(request.getId(), "not found sales order.id");
|
||||||
TimiException.required(request.getId(), "not found sales order.id");
|
GaoSalesOrder order = getForUpdate(request.getId());
|
||||||
GaoSalesOrder order = getForUpdate(request.getId());
|
TimiException.requiredTrue(1 == mapper.updateRemark(order.getId(), request.getRemark()), "订单备注更新失败");
|
||||||
TimiException.requiredTrue(1 == mapper.updateRemark(order.getId(), request.getRemark()), "订单备注更新失败");
|
attachmentService.updateByBizId(Attachment.BizType.GAO_SALES_ORDER, order.getId(), request.getAttachmentList());
|
||||||
attachmentService.updateByBizId(Attachment.BizType.GAO_SALES_ORDER, order.getId(), request.getAttachmentList());
|
return detail(order.getId());
|
||||||
return detail(order.getId());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoSalesOrder createDirectInternal(GaoDirectProductOrderCreateRequest request) {
|
private GaoSalesOrder createDirectInternal(GaoDirectProductOrderCreateRequest request) {
|
||||||
@@ -219,10 +222,11 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
data.getSaleAmount(), data.getItemList());
|
data.getSaleAmount(), data.getItemList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_CANCEL)
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoSalesOrder cancel(GaoSalesOrderCancelRequest request) {
|
public GaoSalesOrder cancel(GaoSalesOrderCancelRequest request) {
|
||||||
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_CANCEL", request, () -> cancelInternal(request));
|
return cancelInternal(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoSalesOrder cancelInternal(GaoSalesOrderCancelRequest request) {
|
private GaoSalesOrder cancelInternal(GaoSalesOrderCancelRequest request) {
|
||||||
@@ -280,18 +284,21 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
if (safe(order.getSaleAmount()) == 0 || safe(order.getPaidAmount()) == safe(order.getSaleAmount())) {
|
if (safe(order.getSaleAmount()) == 0 || safe(order.getPaidAmount()) == safe(order.getSaleAmount())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TimiException.requiredTrue(0 < safe(order.getPaidAmount()), "订单未有已核销收款,不能履约");
|
if (ALLOW_STORE_MANAGER_ZERO_PAYMENT_FULFILLMENT) {
|
||||||
roleChecker.checkAny(ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name());
|
roleChecker.checkAny(ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TimiException.requiredTrue(false, "订单未全额收款,不能发起履约");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int updateItemReservedQuantity(String itemId, BigDecimal delta) {
|
public int updateItemReservedBaseQuantity(String itemId, BigDecimal delta) {
|
||||||
return itemMapper.updateReservedQuantity(itemId, delta);
|
return itemMapper.updateReservedBaseQuantity(itemId, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int updateItemFulfilledQuantity(String itemId, BigDecimal delta) {
|
public int updateItemFulfilledBaseQuantity(String itemId, BigDecimal delta) {
|
||||||
return itemMapper.updateFulfilledQuantity(itemId, delta);
|
return itemMapper.updateFulfilledBaseQuantity(itemId, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -309,7 +316,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
List<GaoSalesOrderItem> itemList = itemMapper.selectBySalesOrderId(order.getId());
|
List<GaoSalesOrderItem> itemList = itemMapper.selectBySalesOrderId(order.getId());
|
||||||
boolean fulfilled = !itemList.isEmpty();
|
boolean fulfilled = !itemList.isEmpty();
|
||||||
for (GaoSalesOrderItem item : itemList) {
|
for (GaoSalesOrderItem item : itemList) {
|
||||||
if (decimal(item.getFulfilledQuantity()).compareTo(decimal(item.getQuantity())) < 0) {
|
if (decimal(item.getFulfilledBaseQuantity()).compareTo(decimal(item.getBaseQuantity())) < 0) {
|
||||||
fulfilled = false;
|
fulfilled = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -383,8 +390,9 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
item.setUnitSnapshot(unit.getName());
|
item.setUnitSnapshot(unit.getName());
|
||||||
item.setBaseQuantityPerUnitSnapshot(unit.getBaseQuantityPerUnit());
|
item.setBaseQuantityPerUnitSnapshot(unit.getBaseQuantityPerUnit());
|
||||||
item.setQuantity(quantity);
|
item.setQuantity(quantity);
|
||||||
item.setReservedQuantity(ZERO);
|
item.setBaseQuantity(toBaseQuantity(quantity, unit.getBaseQuantityPerUnit(), "订单项基础单位数量最多保留三位小数"));
|
||||||
item.setFulfilledQuantity(ZERO);
|
item.setReservedBaseQuantity(ZERO);
|
||||||
|
item.setFulfilledBaseQuantity(ZERO);
|
||||||
item.setUnitPrice(unitPrice);
|
item.setUnitPrice(unitPrice);
|
||||||
item.setLineAmount(lineAmount);
|
item.setLineAmount(lineAmount);
|
||||||
item.setSort(TimiJava.defaultIfNull(source.getSort(), i));
|
item.setSort(TimiJava.defaultIfNull(source.getSort(), i));
|
||||||
@@ -445,20 +453,25 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
for (GaoSalesOrderItem item : itemList) {
|
for (GaoSalesOrderItem item : itemList) {
|
||||||
TimiException.required(item, "销售订单明细不能为空");
|
TimiException.required(item, "销售订单明细不能为空");
|
||||||
TimiException.requiredTrue(item.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
TimiException.requiredTrue(item.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
||||||
|
item.setGifted(Boolean.TRUE.equals(item.getGifted()));
|
||||||
TimiException.required(item.getSkuId(), "订单项 SKU 不能为空");
|
TimiException.required(item.getSkuId(), "订单项 SKU 不能为空");
|
||||||
TimiException.required(item.getSkuUnitId(), "订单项 SKU 单位不能为空");
|
TimiException.required(item.getSkuUnitId(), "订单项 SKU 单位不能为空");
|
||||||
TimiException.requiredTrue(skuUnitKeySet.add(item.getSkuId() + "|" + item.getSkuUnitId()),
|
String skuUnitKey = item.getSkuId() + "|" + item.getSkuUnitId() + "|" + item.getGifted();
|
||||||
|
TimiException.requiredTrue(skuUnitKeySet.add(skuUnitKey),
|
||||||
"同一订单不能重复 SKU 和单位");
|
"同一订单不能重复 SKU 和单位");
|
||||||
TimiException.required(item.getQuantity(), "订单项数量不能为空");
|
TimiException.required(item.getQuantity(), "订单项数量不能为空");
|
||||||
TimiException.requiredTrue(0 < item.getQuantity().signum(), "订单项数量必须大于 0");
|
TimiException.requiredTrue(0 < item.getQuantity().signum(), "订单项数量必须大于 0");
|
||||||
TimiException.requiredTrue(3 >= item.getQuantity().stripTrailingZeros().scale(), "订单项数量最多保留三位小数");
|
TimiException.requiredTrue(3 >= item.getQuantity().stripTrailingZeros().scale(), "订单项数量最多保留三位小数");
|
||||||
TimiException.required(item.getBaseQuantityPerUnitSnapshot(), "订单项单位换算快照不能为空");
|
TimiException.required(item.getBaseQuantityPerUnitSnapshot(), "订单项单位换算快照不能为空");
|
||||||
TimiException.requiredTrue(0 < item.getBaseQuantityPerUnitSnapshot().signum(), "订单项单位换算快照必须大于 0");
|
TimiException.requiredTrue(0 < item.getBaseQuantityPerUnitSnapshot().signum(), "订单项单位换算快照必须大于 0");
|
||||||
TimiException.requiredTrue(item.getReservedQuantity() != null && item.getFulfilledQuantity() != null,
|
TimiException.required(item.getBaseQuantity(), "订单项基础单位数量不能为空");
|
||||||
|
TimiException.requiredTrue(item.getBaseQuantity().compareTo(toBaseQuantity(item.getQuantity(), item.getBaseQuantityPerUnitSnapshot(), "订单项基础单位数量最多保留三位小数")) == 0,
|
||||||
|
"订单项基础单位数量与单位换算快照不一致");
|
||||||
|
TimiException.requiredTrue(item.getReservedBaseQuantity() != null && item.getFulfilledBaseQuantity() != null,
|
||||||
"订单项履约数量不能为空");
|
"订单项履约数量不能为空");
|
||||||
TimiException.requiredTrue(0 <= item.getReservedQuantity().signum() && 0 <= item.getFulfilledQuantity().signum(),
|
TimiException.requiredTrue(0 <= item.getReservedBaseQuantity().signum() && 0 <= item.getFulfilledBaseQuantity().signum(),
|
||||||
"订单项履约数量不能为负数");
|
"订单项履约数量不能为负数");
|
||||||
TimiException.requiredTrue(item.getReservedQuantity().add(item.getFulfilledQuantity()).compareTo(item.getQuantity()) <= 0,
|
TimiException.requiredTrue(item.getReservedBaseQuantity().add(item.getFulfilledBaseQuantity()).compareTo(item.getBaseQuantity()) <= 0,
|
||||||
"订单项履约数量不能超过购买数量");
|
"订单项履约数量不能超过购买数量");
|
||||||
if (expectedItemType == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT) {
|
if (expectedItemType == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT) {
|
||||||
TimiException.requiredNull(item.getUnitPrice(), "套餐订单项不能填写单价");
|
TimiException.requiredNull(item.getUnitPrice(), "套餐订单项不能填写单价");
|
||||||
@@ -500,11 +513,14 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
List<GaoSalesOrderItem> existingItemList = itemMapper.selectBySalesOrderId(existing.getId());
|
List<GaoSalesOrderItem> existingItemList = itemMapper.selectBySalesOrderId(existing.getId());
|
||||||
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应订单内容不一致");
|
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应订单内容不一致");
|
||||||
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream()
|
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream()
|
||||||
.collect(java.util.stream.Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), java.util.function.Function.identity()));
|
.collect(java.util.stream.Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId() + "|" + Boolean.TRUE.equals(item.getGifted()),
|
||||||
|
java.util.function.Function.identity()));
|
||||||
for (GaoSalesOrderItem requestItem : requestItemList) {
|
for (GaoSalesOrderItem requestItem : requestItemList) {
|
||||||
GaoSalesOrderItem existingItem = existingItemMap.get(requestItem.getSkuId() + "|" + requestItem.getSkuUnitId());
|
GaoSalesOrderItem existingItem = existingItemMap.get(requestItem.getSkuId() + "|" + requestItem.getSkuUnitId() + "|"
|
||||||
|
+ Boolean.TRUE.equals(requestItem.getGifted()));
|
||||||
TimiException.required(existingItem, "requestNo 对应订单内容不一致");
|
TimiException.required(existingItem, "requestNo 对应订单内容不一致");
|
||||||
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
||||||
|
&& Boolean.TRUE.equals(existingItem.getGifted()) == Boolean.TRUE.equals(requestItem.getGifted())
|
||||||
&& existingItem.getQuantity().compareTo(requestItem.getQuantity()) == 0
|
&& existingItem.getQuantity().compareTo(requestItem.getQuantity()) == 0
|
||||||
&& existingItem.getSort().equals(requestItem.getSort())
|
&& existingItem.getSort().equals(requestItem.getSort())
|
||||||
&& sameText(existingItem.getRemark(), requestItem.getRemark()), "requestNo 对应订单内容不一致");
|
&& sameText(existingItem.getRemark(), requestItem.getRemark()), "requestNo 对应订单内容不一致");
|
||||||
@@ -544,12 +560,43 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
///
|
///
|
||||||
/// @param item 订单项
|
/// @param item 订单项
|
||||||
private void fillItemBalance(GaoSalesOrderItem item) {
|
private void fillItemBalance(GaoSalesOrderItem item) {
|
||||||
BigDecimal quantity = decimal(item.getQuantity());
|
BigDecimal baseQuantity = decimal(item.getBaseQuantity());
|
||||||
BigDecimal reserved = decimal(item.getReservedQuantity());
|
BigDecimal reservedBaseQuantity = decimal(item.getReservedBaseQuantity());
|
||||||
BigDecimal fulfilled = decimal(item.getFulfilledQuantity());
|
BigDecimal fulfilledBaseQuantity = decimal(item.getFulfilledBaseQuantity());
|
||||||
item.setPendingQuantity(reserved);
|
item.setPendingBaseQuantity(reservedBaseQuantity);
|
||||||
item.setAvailableQuantity(quantity.subtract(reserved).subtract(fulfilled));
|
item.setAvailableBaseQuantity(baseQuantity.subtract(reservedBaseQuantity).subtract(fulfilledBaseQuantity));
|
||||||
item.setRemainingQuantity(quantity.subtract(fulfilled));
|
item.setRemainingBaseQuantity(baseQuantity.subtract(fulfilledBaseQuantity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 填充订单项数量展示所需的 SKU 信息
|
||||||
|
///
|
||||||
|
/// @param itemList 订单项列表
|
||||||
|
private void fillItemDisplayInfo(List<GaoSalesOrderItem> itemList) {
|
||||||
|
if (itemList == null || itemList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<String> skuIdList = itemList.stream()
|
||||||
|
.map(GaoSalesOrderItem::getSkuId)
|
||||||
|
.filter(value -> value != null && !value.isBlank())
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
if (skuIdList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, GaoProductSku> skuMap = skuService.listByIdList(skuIdList).stream()
|
||||||
|
.collect(java.util.stream.Collectors.toMap(GaoProductSku::getId, java.util.function.Function.identity()));
|
||||||
|
for (GaoSalesOrderItem item : itemList) {
|
||||||
|
GaoProductSku sku = skuMap.get(item.getSkuId());
|
||||||
|
if (sku == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
item.setAllowDecimalQuantity(sku.getAllowDecimalQuantity());
|
||||||
|
item.setBaseUnitName(sku.getUnitList().stream()
|
||||||
|
.filter(unit -> Boolean.TRUE.equals(unit.getIsBase()))
|
||||||
|
.map(GaoProductSkuUnit::getName)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillItemAttachments(List<GaoSalesOrderItem> itemList) {
|
private void fillItemAttachments(List<GaoSalesOrderItem> itemList) {
|
||||||
@@ -606,6 +653,24 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 转换基础单位数量
|
||||||
|
///
|
||||||
|
/// @param quantity 当前单位数量
|
||||||
|
/// @param baseQuantityPerUnit 当前单位折算基础单位系数
|
||||||
|
/// @param message 精度错误信息
|
||||||
|
/// @return 三位小数的基础单位数量
|
||||||
|
private BigDecimal toBaseQuantity(BigDecimal quantity, BigDecimal baseQuantityPerUnit, String message) {
|
||||||
|
TimiException.required(quantity, "数量不能为空");
|
||||||
|
TimiException.required(baseQuantityPerUnit, "基础单位数量系数不能为空");
|
||||||
|
BigDecimal result = quantity.multiply(baseQuantityPerUnit);
|
||||||
|
TimiException.requiredTrue(3 >= result.stripTrailingZeros().scale(), message);
|
||||||
|
try {
|
||||||
|
return result.setScale(3, RoundingMode.UNNECESSARY);
|
||||||
|
} catch (ArithmeticException e) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, message, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private long toCentAmount(BigDecimal quantity, long unitPrice) {
|
private long toCentAmount(BigDecimal quantity, long unitPrice) {
|
||||||
try {
|
try {
|
||||||
return BigDecimal.valueOf(unitPrice).multiply(quantity).setScale(0, RoundingMode.UNNECESSARY).longValueExact();
|
return BigDecimal.valueOf(unitPrice).multiply(quantity).setScale(0, RoundingMode.UNNECESSARY).longValueExact();
|
||||||
|
|||||||
+7
@@ -3,6 +3,7 @@ package com.imyeyu.api.modules.gao.service.implement;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreChartService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreChartService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerAgeStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq;
|
||||||
@@ -43,6 +44,12 @@ public class GaoStoreChartServiceImplement implements GaoStoreChartService {
|
|||||||
return customerService.stateGender(storeId);
|
return customerService.stateGender(storeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoCustomerAgeStatView> customerAge(String storeId) {
|
||||||
|
TimiException.required(storeId, "not found storeId");
|
||||||
|
return customerService.stateAge(storeId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoStoreChartReq req) {
|
public List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoStoreChartReq req) {
|
||||||
checkRangeReq(req);
|
checkRangeReq(req);
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 客户年龄统计视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-11
|
||||||
|
@Data
|
||||||
|
public class GaoCustomerAgeStatView {
|
||||||
|
|
||||||
|
/// 客户年龄
|
||||||
|
private Long age;
|
||||||
|
|
||||||
|
/// 该年龄的客户数量
|
||||||
|
private Long count;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 客户每日统计中的客户摘要
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-11
|
||||||
|
@Data
|
||||||
|
public class GaoCustomerDailyStateCustomerView {
|
||||||
|
|
||||||
|
/// 用于服务层按日期分组,不返回给前端
|
||||||
|
@JsonIgnore
|
||||||
|
private Integer dateValue;
|
||||||
|
|
||||||
|
/// 客户姓名
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package com.imyeyu.api.modules.gao.vo;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// 客户每日统计视图
|
/// 客户每日统计视图
|
||||||
///
|
///
|
||||||
@@ -18,4 +20,7 @@ public class GaoCustomerDailyStateView {
|
|||||||
|
|
||||||
/// 截止当日客户总数
|
/// 截止当日客户总数
|
||||||
private Long totalCustomerCount;
|
private Long totalCustomerCount;
|
||||||
|
|
||||||
|
/// 当日新增客户列表
|
||||||
|
private List<GaoCustomerDailyStateCustomerView> customerList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 客户病症原始文本
|
||||||
|
///
|
||||||
|
/// 只用于后端分词,不对外返回。
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-11
|
||||||
|
@Data
|
||||||
|
public class GaoCustomerDiseaseTextView {
|
||||||
|
|
||||||
|
/// 备注
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/// 病症
|
||||||
|
private String disease;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 客户病症词频查询请求
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-11
|
||||||
|
@Data
|
||||||
|
public class GaoCustomerDiseaseWordReq {
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 返回词语数量
|
||||||
|
private Integer topN;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 客户病症词频
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-11
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class GaoCustomerDiseaseWordView {
|
||||||
|
|
||||||
|
/// 词语
|
||||||
|
private String word;
|
||||||
|
|
||||||
|
/// 词语出现次数
|
||||||
|
private Long value;
|
||||||
|
}
|
||||||
@@ -17,6 +17,9 @@ public class GaoCustomerPackagePurchaseItem {
|
|||||||
/// SKU 单位 ID
|
/// SKU 单位 ID
|
||||||
private String skuUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
|
/// true 表示请求将该明细作为赠送权益创建
|
||||||
|
private Boolean gifted;
|
||||||
|
|
||||||
/// 产品名称快照,由后端生成
|
/// 产品名称快照,由后端生成
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.gao.vo;
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
import com.imyeyu.java.bean.BasePage;
|
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||||
|
import com.imyeyu.spring.bean.Page;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ import java.util.List;
|
|||||||
/// @since 2026-08-13
|
/// @since 2026-08-13
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class GaoCustomerPage extends BasePage {
|
public class GaoCustomerPage extends Page<GaoCustomer> {
|
||||||
|
|
||||||
/// 门店 ID
|
/// 门店 ID
|
||||||
private String storeId;
|
private String storeId;
|
||||||
@@ -22,4 +23,10 @@ public class GaoCustomerPage extends BasePage {
|
|||||||
|
|
||||||
/// 标签 ID 列表,客户必须同时拥有列表内全部标签
|
/// 标签 ID 列表,客户必须同时拥有列表内全部标签
|
||||||
private List<String> tagIdList;
|
private List<String> tagIdList;
|
||||||
|
|
||||||
|
/// 录入时间起始,包含,Unix 毫秒时间戳
|
||||||
|
private Long beginCreatedAt;
|
||||||
|
|
||||||
|
/// 录入时间结束,包含,Unix 毫秒时间戳
|
||||||
|
private Long endCreatedAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ public class GaoOrderFulfillmentItemRequest {
|
|||||||
/// 来源销售订单明细 ID
|
/// 来源销售订单明细 ID
|
||||||
private String sourceOrderItemId;
|
private String sourceOrderItemId;
|
||||||
|
|
||||||
|
/// 本次实际履约 SKU 单位 ID
|
||||||
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 本次履约数量
|
/// 本次履约数量
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,6 @@ public class GaoProductPage extends BasePage {
|
|||||||
/// 产品类型
|
/// 产品类型
|
||||||
private GaoProduct.ProductType productType;
|
private GaoProduct.ProductType productType;
|
||||||
|
|
||||||
/// 名称或编码关键词
|
/// 名称、拼音或编码关键词
|
||||||
private String keyword;
|
private String keyword;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ public class GaoProductStockChangeRequest {
|
|||||||
/// 业务数量
|
/// 业务数量
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
|
|
||||||
|
/// 库存发生时间,毫秒时间戳
|
||||||
|
private Long occurredAt;
|
||||||
|
|
||||||
/// 备注
|
/// 备注
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ public class GaoProductStockPage extends BasePage {
|
|||||||
/// 产品 ID
|
/// 产品 ID
|
||||||
private String productId;
|
private String productId;
|
||||||
|
|
||||||
/// 产品或 SKU 名称、编码、规格摘要关键词
|
/// 产品或 SKU 名称、拼音、编码、规格摘要关键词
|
||||||
private String keyword;
|
private String keyword;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class GaoProductStockRecordPage extends BasePage {
|
|||||||
/// 产品 ID
|
/// 产品 ID
|
||||||
private String productId;
|
private String productId;
|
||||||
|
|
||||||
/// 产品名称或编码关键词
|
/// 产品名称、拼音或编码关键词
|
||||||
private String keyword;
|
private String keyword;
|
||||||
|
|
||||||
/// 来源业务类型
|
/// 来源业务类型
|
||||||
|
|||||||
@@ -25,14 +25,15 @@ public class ToolController {
|
|||||||
|
|
||||||
@GetMapping("/memo")
|
@GetMapping("/memo")
|
||||||
public String getMemo() {
|
public String getMemo() {
|
||||||
return settingService.getSystem(Setting.Module.Journal.MEMO).asString();
|
// return settingService.getSystem(Setting.Module.Journal.MEMO).asString();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiredUploadPermission
|
@RequiredUploadPermission
|
||||||
@PostMapping("/memo/update")
|
@PostMapping("/memo/update")
|
||||||
public void updateMemo(@RequestBodyValue String data) {
|
public void updateMemo(@RequestBodyValue String data) {
|
||||||
Setting setting = settingService.getSystem(Setting.Module.Journal.MEMO);
|
// Setting setting = settingService.getSystem(Setting.Module.Journal.MEMO);
|
||||||
setting.setValue(data);
|
// setting.setValue(data);
|
||||||
settingService.update(setting);
|
// settingService.update(setting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.imyeyu.api.modules.music.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.annotation.SettingBizType;
|
||||||
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-04-24 14:52
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@SettingBizType("MUSIC")
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum MusicSetting implements SettingEnum {
|
||||||
|
|
||||||
|
/** 启用服务 */
|
||||||
|
ENABLE(Boolean.FALSE.toString(), Setting.ValueType.BOOLEAN),
|
||||||
|
|
||||||
|
/** 通信消息最大帧长度 */
|
||||||
|
MAX_FRAME_LENGTH("2048", Setting.ValueType.INTEGER),
|
||||||
|
|
||||||
|
/** 受控端 IP */
|
||||||
|
PLAYER_IP("0.0.0.0", Setting.ValueType.STRING),
|
||||||
|
|
||||||
|
/** 受控端端口 */
|
||||||
|
PLAYER_PORT("18392", Setting.ValueType.INTEGER),
|
||||||
|
|
||||||
|
/** 控制端 IP */
|
||||||
|
CONTROLLER_IP("0.0.0.0", Setting.ValueType.STRING),
|
||||||
|
|
||||||
|
/** 控制端端口 */
|
||||||
|
CONTROLLER_PORT("18391", Setting.ValueType.INTEGER),
|
||||||
|
|
||||||
|
/** 控制端入口地址 */
|
||||||
|
CONTROLLER_URI("/music/control", Setting.ValueType.STRING);
|
||||||
|
|
||||||
|
final String defaultValue;
|
||||||
|
|
||||||
|
final Setting.ValueType valueType;
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.music.runner;
|
package com.imyeyu.api.modules.music.runner;
|
||||||
|
|
||||||
import com.imyeyu.api.config.CORSConfig;
|
import com.imyeyu.api.config.CORSConfig;
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
|
import com.imyeyu.api.modules.music.bean.MusicSetting;
|
||||||
import com.imyeyu.api.modules.music.handler.ControllerMessageHandler;
|
import com.imyeyu.api.modules.music.handler.ControllerMessageHandler;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
@@ -62,7 +62,7 @@ public class ControllerBootstrapRunner implements ApplicationRunner, Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run(ApplicationArguments args) {
|
public void run(ApplicationArguments args) {
|
||||||
if (settingService.getSystem(Setting.Module.Music.ENABLE).isTrue()) {
|
if (settingService.getSystem(MusicSetting.ENABLE).isTrue()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ public class ControllerBootstrapRunner implements ApplicationRunner, Application
|
|||||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||||
bootstrap.group(boss, worker);
|
bootstrap.group(boss, worker);
|
||||||
bootstrap.channel(NioServerSocketChannel.class);
|
bootstrap.channel(NioServerSocketChannel.class);
|
||||||
bootstrap.localAddress(new InetSocketAddress(settingService.getSystem(Setting.Module.Music.CONTROLLER_PORT).asInteger()));
|
bootstrap.localAddress(new InetSocketAddress(settingService.getSystem(MusicSetting.CONTROLLER_PORT).asInteger()));
|
||||||
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
|
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -89,7 +89,7 @@ public class ControllerBootstrapRunner implements ApplicationRunner, Application
|
|||||||
@Override
|
@Override
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
|
public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
|
||||||
if (obj instanceof FullHttpRequest req) {
|
if (obj instanceof FullHttpRequest req) {
|
||||||
if (!req.uri().equals(settingService.getSystem(Setting.Module.Music.CONTROLLER_URI).asString())) {
|
if (!req.uri().equals(settingService.getSystem(MusicSetting.CONTROLLER_URI).asString())) {
|
||||||
// 访问的路径不是 Web Socket 的端点地址,响应 404
|
// 访问的路径不是 Web Socket 的端点地址,响应 404
|
||||||
ctx.channel().writeAndFlush(NFP).addListener(ChannelFutureListener.CLOSE);
|
ctx.channel().writeAndFlush(NFP).addListener(ChannelFutureListener.CLOSE);
|
||||||
return;
|
return;
|
||||||
@@ -111,10 +111,10 @@ public class ControllerBootstrapRunner implements ApplicationRunner, Application
|
|||||||
});
|
});
|
||||||
pipeline.addLast(new WebSocketServerCompressionHandler());
|
pipeline.addLast(new WebSocketServerCompressionHandler());
|
||||||
pipeline.addLast(new WebSocketServerProtocolHandler(
|
pipeline.addLast(new WebSocketServerProtocolHandler(
|
||||||
settingService.getSystem(Setting.Module.Music.CONTROLLER_URI).asString(),
|
settingService.getSystem(MusicSetting.CONTROLLER_URI).asString(),
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
settingService.getSystem(Setting.Module.Music.MAX_FRAME_LENGTH).asLong()
|
settingService.getSystem(MusicSetting.MAX_FRAME_LENGTH).asLong()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ public class ControllerBootstrapRunner implements ApplicationRunner, Application
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
channel = bootstrap.bind().sync().channel();
|
channel = bootstrap.bind().sync().channel();
|
||||||
log.info("TimiMusicRC controller service startup with {}", settingService.getSystem(Setting.Module.Music.CONTROLLER_IP).asString());
|
log.info("TimiMusicRC controller service startup with {}", settingService.getSystem(MusicSetting.CONTROLLER_IP).asString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("TimiMusicRC controller service startup error", e);
|
log.error("TimiMusicRC controller service startup error", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.music.runner;
|
package com.imyeyu.api.modules.music.runner;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
|
import com.imyeyu.api.modules.music.bean.MusicSetting;
|
||||||
import com.imyeyu.api.modules.music.handler.PlayerMessageHandler;
|
import com.imyeyu.api.modules.music.handler.PlayerMessageHandler;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
@@ -49,7 +49,7 @@ public class PlayerBootstrapRunner implements ApplicationRunner, ApplicationList
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run(ApplicationArguments args) {
|
public void run(ApplicationArguments args) {
|
||||||
if (settingService.getSystem(Setting.Module.Music.ENABLE).isTrue()) {
|
if (settingService.getSystem(MusicSetting.ENABLE).isTrue()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||||
@@ -58,13 +58,13 @@ public class PlayerBootstrapRunner implements ApplicationRunner, ApplicationList
|
|||||||
try {
|
try {
|
||||||
bootstrap.group(boss, worker);
|
bootstrap.group(boss, worker);
|
||||||
bootstrap.channel(NioServerSocketChannel.class);
|
bootstrap.channel(NioServerSocketChannel.class);
|
||||||
bootstrap.localAddress(new InetSocketAddress(settingService.getSystem(Setting.Module.Music.PLAYER_PORT).asInteger()));
|
bootstrap.localAddress(new InetSocketAddress(settingService.getSystem(MusicSetting.PLAYER_PORT).asInteger()));
|
||||||
bootstrap.childHandler(new ChannelInitializer<>() {
|
bootstrap.childHandler(new ChannelInitializer<>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(Channel ch) {
|
protected void initChannel(Channel ch) {
|
||||||
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
|
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
|
||||||
settingService.getSystem(Setting.Module.Music.MAX_FRAME_LENGTH).asInteger(),
|
settingService.getSystem(MusicSetting.MAX_FRAME_LENGTH).asInteger(),
|
||||||
0,
|
0,
|
||||||
4,
|
4,
|
||||||
0,
|
0,
|
||||||
@@ -79,7 +79,7 @@ public class PlayerBootstrapRunner implements ApplicationRunner, ApplicationList
|
|||||||
|
|
||||||
channel = bootstrap.bind().sync().channel();
|
channel = bootstrap.bind().sync().channel();
|
||||||
|
|
||||||
log.info("TimiMusicRC player service startup with {}", settingService.getSystem(Setting.Module.Music.PLAYER_IP).asString());
|
log.info("TimiMusicRC player service startup with {}", settingService.getSystem(MusicSetting.PLAYER_IP).asString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("TimiMusicRC player service startup error", e);
|
log.error("TimiMusicRC player service startup error", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.imyeyu.api.modules.system.audit;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/// 操作审计编码
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-09
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum AuditOperation {
|
||||||
|
|
||||||
|
COMMON_NOTIFY_CREATE(Logger.Module.COMMON),
|
||||||
|
COMMON_NOTIFY_DELETE(Logger.Module.COMMON),
|
||||||
|
GAO_CUSTOMER_PACKAGE_PURCHASE(Logger.Module.GAO),
|
||||||
|
GAO_CUSTOMER_PACKAGE_CANCEL(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_PAYMENT_RECEIVE(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_PAYMENT_VERIFY(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_REFUND(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_CONFIRM_NO_REFUND(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_FUND_REVOKE(Logger.Module.GAO),
|
||||||
|
GAO_ORDER_FULFILLMENT_CREATE(Logger.Module.GAO),
|
||||||
|
GAO_ORDER_FULFILLMENT_PREPARE_STOCK(Logger.Module.GAO),
|
||||||
|
GAO_ORDER_FULFILLMENT_VERIFY(Logger.Module.GAO),
|
||||||
|
GAO_ORDER_FULFILLMENT_REJECT(Logger.Module.GAO),
|
||||||
|
GAO_ORDER_FULFILLMENT_CANCEL(Logger.Module.GAO),
|
||||||
|
GAO_ORDER_FULFILLMENT_REVOKE(Logger.Module.GAO),
|
||||||
|
GAO_PACKAGE_CONFIG_CREATE(Logger.Module.GAO),
|
||||||
|
GAO_PACKAGE_CONFIG_UPDATE(Logger.Module.GAO),
|
||||||
|
GAO_PACKAGE_CONFIG_DELETE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_CATEGORY_SORT(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_CATEGORY_CREATE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_CATEGORY_UPDATE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_CATEGORY_DISABLE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_CREATE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_UPDATE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_DISABLE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_STOCK_INITIAL_IN(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_STOCK_MANUAL_IN(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_STOCK_MANUAL_OUT(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_STOCK_MANUAL_REVOKE(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_STOCK_STOCKTAKE_GAIN(Logger.Module.GAO),
|
||||||
|
GAO_PRODUCT_STOCK_STOCKTAKE_LOSS(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_UPDATE(Logger.Module.GAO),
|
||||||
|
GAO_SALES_ORDER_CANCEL(Logger.Module.GAO);
|
||||||
|
|
||||||
|
final Logger.Module module;
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package com.imyeyu.api.modules.system.audit;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.imyeyu.spring.entity.IDEntity;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/// 操作审计参数和结果序列化器
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-09
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuditPayloadSerializer {
|
||||||
|
|
||||||
|
private static final int MAX_LENGTH = 32 * 1024;
|
||||||
|
|
||||||
|
private static final Set<String> SECRET_FIELD_SET = Set.of(
|
||||||
|
"password",
|
||||||
|
"passwordconfirm",
|
||||||
|
"token",
|
||||||
|
"accesstoken",
|
||||||
|
"refreshtoken",
|
||||||
|
"secret",
|
||||||
|
"credential",
|
||||||
|
"authorization"
|
||||||
|
);
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
/// 序列化方法参数
|
||||||
|
///
|
||||||
|
/// @param arguments 方法参数
|
||||||
|
/// @return 脱敏后的参数 JSON
|
||||||
|
public String serializeArguments(Object[] arguments) {
|
||||||
|
if (arguments == null || 0 == arguments.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return serialize(arguments.length == 1 ? arguments[0] : arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 序列化业务结果
|
||||||
|
///
|
||||||
|
/// @param result 方法返回值
|
||||||
|
/// @param arguments 方法参数
|
||||||
|
/// @return 结果摘要
|
||||||
|
public String serializeResult(Object result, Object[] arguments) {
|
||||||
|
if (result instanceof IDEntity<?> entity && entity.getId() != null) {
|
||||||
|
return truncate(String.valueOf(entity.getId()));
|
||||||
|
}
|
||||||
|
if (result != null) {
|
||||||
|
return summarize(result);
|
||||||
|
}
|
||||||
|
if (arguments != null) {
|
||||||
|
for (Object argument : arguments) {
|
||||||
|
if (argument instanceof IDEntity<?> entity && entity.getId() != null) {
|
||||||
|
return truncate(String.valueOf(entity.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (1 == arguments.length && arguments[0] instanceof CharSequence value) {
|
||||||
|
return truncate(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String serialize(Object value) {
|
||||||
|
try {
|
||||||
|
JsonNode node = objectMapper.valueToTree(value);
|
||||||
|
mask(node);
|
||||||
|
return truncate(objectMapper.writeValueAsString(node));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return value == null ? null : truncate(value.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mask(JsonNode node) {
|
||||||
|
if (node == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (node.isObject()) {
|
||||||
|
ObjectNode objectNode = (ObjectNode) node;
|
||||||
|
objectNode.fieldNames().forEachRemaining(fieldName -> {
|
||||||
|
JsonNode field = objectNode.get(fieldName);
|
||||||
|
if (SECRET_FIELD_SET.contains(fieldName.toLowerCase(Locale.ROOT))) {
|
||||||
|
objectNode.put(fieldName, "***");
|
||||||
|
} else {
|
||||||
|
mask(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (node.isArray()) {
|
||||||
|
node.forEach(this::mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String summarize(Object value) {
|
||||||
|
if (value instanceof CharSequence || value instanceof Number || value instanceof Boolean || value instanceof Enum<?>) {
|
||||||
|
return truncate(String.valueOf(value));
|
||||||
|
}
|
||||||
|
if (value instanceof Iterable<?> iterable) {
|
||||||
|
int size = 0;
|
||||||
|
for (Object ignored : iterable) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
return "%s[%s]".formatted(value.getClass().getSimpleName(), size);
|
||||||
|
}
|
||||||
|
if (value instanceof java.util.Map<?, ?> map) {
|
||||||
|
return "%s[%s]".formatted(value.getClass().getSimpleName(), map.size());
|
||||||
|
}
|
||||||
|
if (value.getClass().isArray()) {
|
||||||
|
return "%s[%s]".formatted(value.getClass().getComponentType().getSimpleName(), Array.getLength(value));
|
||||||
|
}
|
||||||
|
return value.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String truncate(String value) {
|
||||||
|
if (value == null || value.length() <= MAX_LENGTH) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return value.substring(0, MAX_LENGTH) + "...";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.imyeyu.api.modules.system.audit;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
|
|
||||||
|
/// 操作审计记录器
|
||||||
|
///
|
||||||
|
/// 业务切面只依赖该写入端口,不直接依赖日志查询服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-09
|
||||||
|
public interface AuditRecorder {
|
||||||
|
|
||||||
|
/// 记录操作审计
|
||||||
|
///
|
||||||
|
/// @param logger 审计记录
|
||||||
|
void record(Logger logger);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.imyeyu.api.modules.system.audit;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/// 标记需要记录操作审计的应用服务方法
|
||||||
|
///
|
||||||
|
/// 只应标记对外的业务操作入口,不要标记被入口调用的内部方法
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-09
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface OperationAudit {
|
||||||
|
|
||||||
|
/// 操作编码
|
||||||
|
///
|
||||||
|
/// @return 操作编码
|
||||||
|
AuditOperation value();
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.imyeyu.api.modules.system.audit;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/// 操作审计切面
|
||||||
|
///
|
||||||
|
/// 该切面放在事务拦截器外层,确保成功日志只在事务提交成功后生成
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-09
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@Order(Ordered.LOWEST_PRECEDENCE - 100)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class OperationAuditAspect {
|
||||||
|
|
||||||
|
private final AuditRecorder auditRecorder;
|
||||||
|
|
||||||
|
private final AuditPayloadSerializer payloadSerializer;
|
||||||
|
|
||||||
|
/// 执行并记录操作审计
|
||||||
|
///
|
||||||
|
/// @param joinPoint 连接点
|
||||||
|
/// @param operationAudit 审计配置
|
||||||
|
/// @return 业务返回值
|
||||||
|
/// @throws Throwable 业务异常
|
||||||
|
@Around("@annotation(operationAudit)")
|
||||||
|
public Object around(ProceedingJoinPoint joinPoint, OperationAudit operationAudit) throws Throwable {
|
||||||
|
AuditOperation operation = operationAudit.value();
|
||||||
|
Object[] arguments = joinPoint.getArgs();
|
||||||
|
Logger logger = new Logger(operation.getModule(), operation.name());
|
||||||
|
logger.setContent(payloadSerializer.serializeArguments(arguments));
|
||||||
|
long startNanos = System.nanoTime();
|
||||||
|
try {
|
||||||
|
Object result = joinPoint.proceed();
|
||||||
|
logger.setLevel(Logger.Level.INFO);
|
||||||
|
logger.setResult(payloadSerializer.serializeResult(result, arguments));
|
||||||
|
logger.setDuration(duration(startNanos));
|
||||||
|
record(logger);
|
||||||
|
return result;
|
||||||
|
} catch (TimiException e) {
|
||||||
|
logger.setLevel(Logger.Level.WARN);
|
||||||
|
logger.setException(e.getMessage());
|
||||||
|
logger.setDuration(duration(startNanos));
|
||||||
|
record(logger);
|
||||||
|
throw e;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.setLevel(Logger.Level.ERROR);
|
||||||
|
logger.setException(TimiJava.serializeThrowable(e));
|
||||||
|
logger.setDuration(duration(startNanos));
|
||||||
|
log.error("业务操作失败: {}", operation.name(), e);
|
||||||
|
record(logger);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void record(Logger logger) {
|
||||||
|
try {
|
||||||
|
auditRecorder.record(logger);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("记录操作审计失败: {}", logger.getOperation(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int duration(long startNanos) {
|
||||||
|
long millis = (System.nanoTime() - startNanos) / 1_000_000L;
|
||||||
|
if (Integer.MAX_VALUE < millis) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return (int) Math.max(0L, millis);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ public class ServerFile implements Serializable {
|
|||||||
name = file.getName();
|
name = file.getName();
|
||||||
absolutePath = file.getAbsolutePath().replaceAll("\\\\", "/");
|
absolutePath = file.getAbsolutePath().replaceAll("\\\\", "/");
|
||||||
SettingService settingService = TimiServerAPI.applicationContext.getBean(SettingService.class);
|
SettingService settingService = TimiServerAPI.applicationContext.getBean(SettingService.class);
|
||||||
absolutePath = absolutePath.substring("E:\\IDEAProject\\TimiServerAPI\\data\\file".length());
|
absolutePath = absolutePath.substring(new File(settingService.getSystem(SystemFileSetting.BASE_PATH).asString()).getAbsolutePath().length());
|
||||||
|
|
||||||
modifiedAt = file.lastModified();
|
modifiedAt = file.lastModified();
|
||||||
this.file = file.isFile();
|
this.file = file.isFile();
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.imyeyu.api.modules.system.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// @author 夜雨
|
||||||
|
/// @since 2026-09-08 17:08
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum SystemFileSetting implements SettingEnum {
|
||||||
|
|
||||||
|
BASE_PATH("data/file", Setting.ValueType.STRING);
|
||||||
|
|
||||||
|
final String defaultValue;
|
||||||
|
|
||||||
|
final Setting.ValueType valueType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.imyeyu.api.modules.system.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.annotation.SettingBizType;
|
||||||
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-04-24 14:23
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@SettingBizType("SYSTEM_STATUS")
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum SystemStatusSetting implements SettingEnum {
|
||||||
|
|
||||||
|
/** 状态采集数据量 */
|
||||||
|
LIMIT("512", Setting.ValueType.INTEGER),
|
||||||
|
|
||||||
|
/** 状态采集网卡 */
|
||||||
|
NETWORK_MAC(null, Setting.ValueType.STRING),
|
||||||
|
|
||||||
|
/** 状态采集频率 */
|
||||||
|
RATE("3000", Setting.ValueType.INTEGER);
|
||||||
|
|
||||||
|
final String defaultValue;
|
||||||
|
|
||||||
|
final Setting.ValueType valueType;
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.system.controller;
|
package com.imyeyu.api.modules.system.controller;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.common.service.TagService;
|
|
||||||
import com.imyeyu.api.modules.system.bean.ServerFile;
|
import com.imyeyu.api.modules.system.bean.ServerFile;
|
||||||
|
import com.imyeyu.api.modules.system.bean.SystemFileSetting;
|
||||||
import com.imyeyu.api.modules.system.bean.TransferFile;
|
import com.imyeyu.api.modules.system.bean.TransferFile;
|
||||||
import com.imyeyu.api.modules.system.service.AsyncTaskService;
|
import com.imyeyu.api.modules.system.service.AsyncTaskService;
|
||||||
import com.imyeyu.api.modules.system.service.FileService;
|
import com.imyeyu.api.modules.system.service.FileService;
|
||||||
@@ -47,7 +46,6 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -67,7 +65,6 @@ import java.util.Map;
|
|||||||
@RequestMapping("/system/file")
|
@RequestMapping("/system/file")
|
||||||
public class FileController implements TimiJava, OS.FileSystem {
|
public class FileController implements TimiJava, OS.FileSystem {
|
||||||
|
|
||||||
private final TagService tagService;
|
|
||||||
private final FileService service;
|
private final FileService service;
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
private final AsyncTaskService asyncTaskService;
|
private final AsyncTaskService asyncTaskService;
|
||||||
@@ -143,7 +140,7 @@ public class FileController implements TimiJava, OS.FileSystem {
|
|||||||
public void read(HttpServletRequest req, HttpServletResponse resp) {
|
public void read(HttpServletRequest req, HttpServletResponse resp) {
|
||||||
try {
|
try {
|
||||||
String path = req.getServletPath().substring("/system/file/read".length());
|
String path = req.getServletPath().substring("/system/file/read".length());
|
||||||
path = "/root/bin/data/mnt" + path;
|
path = settingService.getSystem(SystemFileSetting.BASE_PATH).asString() + path;
|
||||||
if (TimiJava.isEmpty(path)) {
|
if (TimiJava.isEmpty(path)) {
|
||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
@@ -195,8 +192,7 @@ public class FileController implements TimiJava, OS.FileSystem {
|
|||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// path = settingService.getAsString(Setting.Module.System.FILE_BASE_PATH) + path;
|
File file = new File(settingService.getSystem(SystemFileSetting.BASE_PATH).asString() + path);
|
||||||
File file = new File(path);
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
log.warn("preview a not exist file: {}", path);
|
log.warn("preview a not exist file: {}", path);
|
||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
@@ -409,8 +405,7 @@ public class FileController implements TimiJava, OS.FileSystem {
|
|||||||
public void download(HttpServletResponse resp) {
|
public void download(HttpServletResponse resp) {
|
||||||
try {
|
try {
|
||||||
String path = TimiSpring.cutURIStartAt("/system/file/download");
|
String path = TimiSpring.cutURIStartAt("/system/file/download");
|
||||||
path = "E:/IDEAProject/TimiServerAPI/data/file" + path;
|
File file = new File(settingService.getSystem(SystemFileSetting.BASE_PATH).asString() + path);
|
||||||
File file = new File(path);
|
|
||||||
service.checkAccessPermission(file.getAbsolutePath());
|
service.checkAccessPermission(file.getAbsolutePath());
|
||||||
|
|
||||||
String mimeType = new Tika().detect(file);
|
String mimeType = new Tika().detect(file);
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.system.controller;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-04-23 16:31
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RequestMapping("/system/setting")
|
|
||||||
public class SettingController {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -6,8 +6,6 @@ import com.imyeyu.spring.service.DestroyableService;
|
|||||||
import com.imyeyu.spring.service.GettableService;
|
import com.imyeyu.spring.service.GettableService;
|
||||||
import com.imyeyu.spring.service.PageableService;
|
import com.imyeyu.spring.service.PageableService;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志服务
|
* 日志服务
|
||||||
*
|
*
|
||||||
@@ -18,51 +16,6 @@ public interface LoggerService extends GettableService<Logger, String>, Pageable
|
|||||||
|
|
||||||
void createSync(Logger logger);
|
void createSync(Logger logger);
|
||||||
|
|
||||||
/// 执行并记录有返回值的业务操作
|
|
||||||
///
|
|
||||||
/// @param logger 日志对象,调用方负责设置模块、操作和请求内容
|
|
||||||
/// @param action 业务操作
|
|
||||||
/// @param <T> 返回值类型
|
|
||||||
/// @return 业务操作返回值
|
|
||||||
<T> T execute(Logger logger, Supplier<T> action);
|
|
||||||
|
|
||||||
/// 执行并记录无返回值的业务操作
|
|
||||||
///
|
|
||||||
/// @param logger 日志对象,调用方负责设置模块、操作和请求内容
|
|
||||||
/// @param action 业务操作
|
|
||||||
default void execute(Logger logger, Runnable action) {
|
|
||||||
execute(logger, () -> {
|
|
||||||
action.run();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 创建日志并执行有返回值的业务操作
|
|
||||||
///
|
|
||||||
/// @param module 日志模块
|
|
||||||
/// @param operation 操作描述
|
|
||||||
/// @param content 请求内容
|
|
||||||
/// @param action 业务操作
|
|
||||||
/// @param <T> 返回值类型
|
|
||||||
/// @return 业务操作返回值
|
|
||||||
default <T> T execute(Logger.Module module, String operation, Object content, Supplier<T> action) {
|
|
||||||
Logger logger = new Logger(module, operation);
|
|
||||||
logger.setContent(content == null ? null : content.toString());
|
|
||||||
return execute(logger, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 创建日志并执行无返回值的业务操作
|
|
||||||
///
|
|
||||||
/// @param module 日志模块
|
|
||||||
/// @param operation 操作描述
|
|
||||||
/// @param content 请求内容
|
|
||||||
/// @param action 业务操作
|
|
||||||
default void execute(Logger.Module module, String operation, Object content, Runnable action) {
|
|
||||||
Logger logger = new Logger(module, operation);
|
|
||||||
logger.setContent(content == null ? null : content.toString());
|
|
||||||
execute(logger, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void create(Logger.Module module, Logger.Level level, String operation, String content, String result);
|
void create(Logger.Module module, Logger.Level level, String operation, String content, String result);
|
||||||
|
|
||||||
default void info(Logger.Module module, String operation) {
|
default void info(Logger.Module module, String operation) {
|
||||||
|
|||||||
+2
-3
@@ -1,8 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.system.service.implement;
|
package com.imyeyu.api.modules.system.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.system.bean.ServerFile;
|
import com.imyeyu.api.modules.system.bean.ServerFile;
|
||||||
|
import com.imyeyu.api.modules.system.bean.SystemFileSetting;
|
||||||
import com.imyeyu.api.modules.system.bean.TransferFile;
|
import com.imyeyu.api.modules.system.bean.TransferFile;
|
||||||
import com.imyeyu.api.modules.system.service.AsyncTaskService;
|
import com.imyeyu.api.modules.system.service.AsyncTaskService;
|
||||||
import com.imyeyu.api.modules.system.service.FileService;
|
import com.imyeyu.api.modules.system.service.FileService;
|
||||||
@@ -257,8 +257,7 @@ public class FileServiceImplement implements TimiJava, FileService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getPath(String path) {
|
private String getPath(String path) {
|
||||||
// String basePath = settingService.getSystem(Setting.Module.System.FILE_BASE_PATH).asString();
|
String basePath = settingService.getSystem(SystemFileSetting.BASE_PATH).asString();
|
||||||
String basePath = "E:\\IDEAProject\\TimiServerAPI\\data\\file";
|
|
||||||
if (TimiJava.isEmpty(path)) {
|
if (TimiJava.isEmpty(path)) {
|
||||||
return basePath;
|
return basePath;
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-37
@@ -1,37 +1,41 @@
|
|||||||
package com.imyeyu.api.modules.system.service.implement;
|
package com.imyeyu.api.modules.system.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
|
import com.imyeyu.api.modules.system.audit.AuditRecorder;
|
||||||
import com.imyeyu.api.modules.system.mapper.LoggerMapper;
|
import com.imyeyu.api.modules.system.mapper.LoggerMapper;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||||
import com.imyeyu.api.modules.user.entity.User;
|
import com.imyeyu.api.modules.user.entity.User;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.spring.TimiSpring;
|
import com.imyeyu.spring.TimiSpring;
|
||||||
import com.imyeyu.spring.entity.IDEntity;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import com.imyeyu.spring.service.AbstractEntityService;
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志服务实现
|
* 日志服务实现
|
||||||
*
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2026-05-13 14:46
|
* @since 2026-05-13 14:46
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
public class LoggerServiceImplement extends AbstractEntityService<Logger, String> implements LoggerService, AuditRecorder {
|
||||||
public class LoggerServiceImplement extends AbstractEntityService<Logger, String> implements LoggerService {
|
|
||||||
|
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
|
|
||||||
private final LoggerMapper mapper;
|
private final LoggerMapper mapper;
|
||||||
|
|
||||||
|
private final ThreadPoolTaskExecutor auditTaskExecutor;
|
||||||
|
|
||||||
|
public LoggerServiceImplement(UserLoginService userLoginService, LoggerMapper mapper,
|
||||||
|
@Qualifier("auditTaskExecutor") ThreadPoolTaskExecutor auditTaskExecutor) {
|
||||||
|
this.userLoginService = userLoginService;
|
||||||
|
this.mapper = mapper;
|
||||||
|
this.auditTaskExecutor = auditTaskExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<Logger, String> mapper() {
|
protected BaseMapper<Logger, String> mapper() {
|
||||||
return mapper;
|
return mapper;
|
||||||
@@ -39,9 +43,13 @@ public class LoggerServiceImplement extends AbstractEntityService<Logger, String
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void create(Logger logger) {
|
public void create(Logger logger) {
|
||||||
// 默认异步
|
|
||||||
fillContext(logger);
|
fillContext(logger);
|
||||||
Thread.startVirtualThread(() -> createSync(logger));
|
auditTaskExecutor.execute(() -> createSync(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void record(Logger logger) {
|
||||||
|
create(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,32 +58,6 @@ public class LoggerServiceImplement extends AbstractEntityService<Logger, String
|
|||||||
super.create(logger);
|
super.create(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T execute(Logger logger, Supplier<T> action) {
|
|
||||||
try {
|
|
||||||
T result = action.get();
|
|
||||||
logger.setLevel(Logger.Level.INFO);
|
|
||||||
if (result instanceof IDEntity<?> entity) {
|
|
||||||
logger.setResult(String.valueOf(entity.getId()));
|
|
||||||
} else if (result != null) {
|
|
||||||
logger.setResult(result.toString());
|
|
||||||
}
|
|
||||||
create(logger);
|
|
||||||
return result;
|
|
||||||
} catch (TimiException e) {
|
|
||||||
logger.setLevel(Logger.Level.WARN);
|
|
||||||
logger.setException(e.getMessage());
|
|
||||||
create(logger);
|
|
||||||
throw e;
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
logger.setLevel(Logger.Level.ERROR);
|
|
||||||
logger.setException(TimiJava.serializeThrowable(e));
|
|
||||||
log.error("业务操作失败: {}", logger.getOperation(), e);
|
|
||||||
create(logger);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void create(Logger.Module module, Logger.Level level, String operation, String content, String result) {
|
public void create(Logger.Module module, Logger.Level level, String operation, String content, String result) {
|
||||||
Logger log = new Logger(module, level, operation);
|
Logger log = new Logger(module, level, operation);
|
||||||
log.setContent(content);
|
log.setContent(content);
|
||||||
|
|||||||
+5
-5
@@ -1,8 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.system.service.implement;
|
package com.imyeyu.api.modules.system.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.system.bean.ServerStatus;
|
import com.imyeyu.api.modules.system.bean.ServerStatus;
|
||||||
|
import com.imyeyu.api.modules.system.bean.SystemStatusSetting;
|
||||||
import com.imyeyu.api.modules.system.service.StatusService;
|
import com.imyeyu.api.modules.system.service.StatusService;
|
||||||
import com.imyeyu.api.modules.system.vo.SystemStatusDataView;
|
import com.imyeyu.api.modules.system.vo.SystemStatusDataView;
|
||||||
import com.imyeyu.api.modules.system.vo.SystemStatusHistoryView;
|
import com.imyeyu.api.modules.system.vo.SystemStatusHistoryView;
|
||||||
@@ -33,7 +33,7 @@ public class StatusServiceImplement implements StatusService {
|
|||||||
@Override
|
@Override
|
||||||
public SystemStatusSnapshotView getStatus(String metrics) {
|
public SystemStatusSnapshotView getStatus(String metrics) {
|
||||||
long serverTime = Time.now();
|
long serverTime = Time.now();
|
||||||
int sampleRateMs = settingService.getSystem(Setting.Module.SystemStatus.RATE).asInteger();
|
int sampleRateMs = settingService.getSystem(SystemStatusSetting.RATE).asInteger();
|
||||||
EnumSet<Metric> selectedMetrics = parseMetrics(metrics);
|
EnumSet<Metric> selectedMetrics = parseMetrics(metrics);
|
||||||
|
|
||||||
synchronized (serverStatus) {
|
synchronized (serverStatus) {
|
||||||
@@ -48,7 +48,7 @@ public class StatusServiceImplement implements StatusService {
|
|||||||
@Override
|
@Override
|
||||||
public SystemStatusHistoryView getStatusHistory(String window, String metrics) {
|
public SystemStatusHistoryView getStatusHistory(String window, String metrics) {
|
||||||
long serverTime = Time.now();
|
long serverTime = Time.now();
|
||||||
int sampleRateMs = settingService.getSystem(Setting.Module.SystemStatus.RATE).asInteger();
|
int sampleRateMs = settingService.getSystem(SystemStatusSetting.RATE).asInteger();
|
||||||
EnumSet<Metric> selectedMetrics = parseMetrics(metrics);
|
EnumSet<Metric> selectedMetrics = parseMetrics(metrics);
|
||||||
|
|
||||||
synchronized (serverStatus) {
|
synchronized (serverStatus) {
|
||||||
@@ -261,7 +261,7 @@ public class StatusServiceImplement implements StatusService {
|
|||||||
*/
|
*/
|
||||||
private long parseWindowMs(String window, int sampleRateMs) {
|
private long parseWindowMs(String window, int sampleRateMs) {
|
||||||
if (window == null || window.isBlank()) {
|
if (window == null || window.isBlank()) {
|
||||||
return (long) settingService.getSystem(Setting.Module.SystemStatus.RATE).asInteger() * sampleRateMs;
|
return (long) settingService.getSystem(SystemStatusSetting.RATE).asInteger() * sampleRateMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
String normalized = window.trim().toLowerCase(Locale.ROOT);
|
String normalized = window.trim().toLowerCase(Locale.ROOT);
|
||||||
@@ -277,7 +277,7 @@ public class StatusServiceImplement implements StatusService {
|
|||||||
try {
|
try {
|
||||||
return Math.max(sampleRateMs, Long.parseLong(valueText) * unit);
|
return Math.max(sampleRateMs, Long.parseLong(valueText) * unit);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return (long) settingService.getSystem(Setting.Module.SystemStatus.RATE).asInteger() * sampleRateMs;
|
return (long) settingService.getSystem(SystemStatusSetting.RATE).asInteger() * sampleRateMs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.system.task.status;
|
package com.imyeyu.api.modules.system.task.status;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
import com.imyeyu.api.modules.system.bean.SystemStatusSetting;
|
||||||
|
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public abstract class AbstractDequeStatusCollector implements StatusCollector {
|
|||||||
*/
|
*/
|
||||||
protected <T> void putDeque(StatusCollectContext context, Deque<T> deque, T value) {
|
protected <T> void putDeque(StatusCollectContext context, Deque<T> deque, T value) {
|
||||||
deque.addLast(value);
|
deque.addLast(value);
|
||||||
if (context.getSettingService().getSystem(Setting.Module.SystemStatus.LIMIT).asInteger() < deque.size()) {
|
if (context.getSettingService().getSystem(SystemStatusSetting.LIMIT).asInteger() < deque.size()) {
|
||||||
deque.pollFirst();
|
deque.pollFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.system.task.status.collector;
|
package com.imyeyu.api.modules.system.task.status.collector;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
import com.imyeyu.api.modules.system.bean.SystemStatusSetting;
|
||||||
import com.imyeyu.api.modules.system.task.status.AbstractDequeStatusCollector;
|
import com.imyeyu.api.modules.system.task.status.AbstractDequeStatusCollector;
|
||||||
import com.imyeyu.api.modules.system.task.status.StatusCollectContext;
|
import com.imyeyu.api.modules.system.task.status.StatusCollectContext;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -24,7 +24,7 @@ public class NetworkStatusCollector extends AbstractDequeStatusCollector {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(StatusCollectContext context) {
|
public void initialize(StatusCollectContext context) {
|
||||||
List<NetworkIF> networkIFs = context.getHardware().getNetworkIFs();
|
List<NetworkIF> networkIFs = context.getHardware().getNetworkIFs();
|
||||||
String targetMac = context.getSettingService().getSystem(Setting.Module.SystemStatus.NETWORK_MAC).asString();
|
String targetMac = context.getSettingService().getSystem(SystemStatusSetting.NETWORK_MAC).asString();
|
||||||
for (NetworkIF networkIF : networkIFs) {
|
for (NetworkIF networkIF : networkIFs) {
|
||||||
if (networkIF.getMacaddr().equals(targetMac)) {
|
if (networkIF.getMacaddr().equals(targetMac)) {
|
||||||
networkIF.updateAttributes();
|
networkIF.updateAttributes();
|
||||||
@@ -41,8 +41,8 @@ public class NetworkStatusCollector extends AbstractDequeStatusCollector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void collect(StatusCollectContext context) {
|
public void collect(StatusCollectContext context) {
|
||||||
String targetMac = context.getSettingService().getSystem(Setting.Module.SystemStatus.NETWORK_MAC).asString();
|
String targetMac = context.getSettingService().getSystem(SystemStatusSetting.NETWORK_MAC).asString();
|
||||||
int sampleRateMs = context.getSettingService().getSystem(Setting.Module.SystemStatus.RATE).asInteger();
|
int sampleRateMs = context.getSettingService().getSystem(SystemStatusSetting.RATE).asInteger();
|
||||||
for (NetworkIF networkIF : context.getHardware().getNetworkIFs()) {
|
for (NetworkIF networkIF : context.getHardware().getNetworkIFs()) {
|
||||||
if (networkIF.getMacaddr().equals(targetMac)) {
|
if (networkIF.getMacaddr().equals(targetMac)) {
|
||||||
networkIF.updateAttributes();
|
networkIF.updateAttributes();
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.imyeyu.api.modules.user.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.annotation.SettingBizType;
|
||||||
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-04-24 15:03
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@SettingBizType("USER")
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum UserSetting implements SettingEnum {
|
||||||
|
|
||||||
|
/** 启用登录 */
|
||||||
|
ENABLE_LOGIN(Boolean.TRUE.toString(), Setting.ValueType.BOOLEAN),
|
||||||
|
|
||||||
|
/** 启用注册 */
|
||||||
|
ENABLE_REGISTER(Boolean.TRUE.toString(), Setting.ValueType.BOOLEAN),
|
||||||
|
|
||||||
|
/** 允许数据更新 */
|
||||||
|
ENABLE_PROFILE_UPDATE(Boolean.TRUE.toString(), Setting.ValueType.BOOLEAN),
|
||||||
|
|
||||||
|
/** 令牌有效期 */
|
||||||
|
TOKEN_TTL("7d", Setting.ValueType.TIME);
|
||||||
|
|
||||||
|
final String defaultValue;
|
||||||
|
|
||||||
|
final Setting.ValueType valueType;
|
||||||
|
}
|
||||||
+2
-2
@@ -3,12 +3,12 @@ package com.imyeyu.api.modules.user.service.implement;
|
|||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
import com.imyeyu.api.modules.common.entity.NotifyDetail;
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.common.service.SmsService;
|
import com.imyeyu.api.modules.common.service.SmsService;
|
||||||
import com.imyeyu.api.modules.common.vo.SmsCaptchaVerifyRequest;
|
import com.imyeyu.api.modules.common.vo.SmsCaptchaVerifyRequest;
|
||||||
import com.imyeyu.api.modules.user.bean.BootstrapData;
|
import com.imyeyu.api.modules.user.bean.BootstrapData;
|
||||||
|
import com.imyeyu.api.modules.user.bean.UserSetting;
|
||||||
import com.imyeyu.api.modules.user.entity.User;
|
import com.imyeyu.api.modules.user.entity.User;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.api.modules.user.service.UserRoleService;
|
import com.imyeyu.api.modules.user.service.UserRoleService;
|
||||||
@@ -79,7 +79,7 @@ public class UserLoginServiceImplement implements UserLoginService, TimiJava {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoginResponse login(String token, String userId) {
|
public LoginResponse login(String token, String userId) {
|
||||||
Long time = settingService.getSystem(Setting.Module.User.TOKEN_TTL).asTime();
|
Long time = settingService.getSystem(UserSetting.TOKEN_TTL).asTime();
|
||||||
long now = Time.now();
|
long now = Time.now();
|
||||||
long expireAt = now + time;
|
long expireAt = now + time;
|
||||||
redisUserToken.set(getUserTokenKey(token), userId, expireAt - now);
|
redisUserToken.set(getUserTokenKey(token), userId, expireAt - now);
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.imyeyu.api.util;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.SettingEnum;
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 启动时初始化内建系统配置
|
||||||
|
///
|
||||||
|
/// @author 夜雨
|
||||||
|
/// @since 2026-09-08
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public class InitBuiltinSetting implements ApplicationRunner {
|
||||||
|
|
||||||
|
/// 设置枚举所在的扫描包
|
||||||
|
private static final String SCAN_PACKAGE = "com.imyeyu.api";
|
||||||
|
|
||||||
|
private final SettingService settingService;
|
||||||
|
|
||||||
|
/// 初始化所有设置枚举对应的系统配置
|
||||||
|
///
|
||||||
|
/// @param args 启动参数
|
||||||
|
/// @throws Exception 初始化异常
|
||||||
|
@Override
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
public void run(ApplicationArguments args) throws Exception {
|
||||||
|
for (SettingEnum item : findSettingEnumList()) {
|
||||||
|
if (settingService.getSystem(item) == null) {
|
||||||
|
Setting setting = new Setting();
|
||||||
|
setting.setBizType(item.getBizType());
|
||||||
|
setting.setBizId(TimiJava.ZERO_UUID);
|
||||||
|
setting.setCode(item.name());
|
||||||
|
setting.setValue(item.getDefaultValue());
|
||||||
|
setting.setValueType(item.getValueType());
|
||||||
|
setting.setIsPrivate(true);
|
||||||
|
settingService.create(setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 扫描所有实现设置枚举接口的枚举常量
|
||||||
|
///
|
||||||
|
/// @return 设置枚举常量列表
|
||||||
|
/// @throws ClassNotFoundException 枚举类无法加载
|
||||||
|
private List<SettingEnum> findSettingEnumList() throws ClassNotFoundException {
|
||||||
|
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
|
||||||
|
scanner.addIncludeFilter(new AssignableTypeFilter(SettingEnum.class));
|
||||||
|
|
||||||
|
List<BeanDefinition> candidateList = new ArrayList<>(scanner.findCandidateComponents(SCAN_PACKAGE));
|
||||||
|
List<SettingEnum> result = new ArrayList<>();
|
||||||
|
for (BeanDefinition candidate : candidateList) {
|
||||||
|
String className = candidate.getBeanClassName();
|
||||||
|
if (className == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Class<?> candidateClass = Class.forName(className);
|
||||||
|
if (!candidateClass.isEnum() || !SettingEnum.class.isAssignableFrom(candidateClass)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Object constant : candidateClass.getEnumConstants()) {
|
||||||
|
result.add((SettingEnum) constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package db.migration.timiserver;
|
||||||
|
|
||||||
|
import com.imyeyu.api.util.PinyinUtil;
|
||||||
|
import org.flywaydb.core.api.migration.BaseJavaMigration;
|
||||||
|
import org.flywaydb.core.api.migration.Context;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
|
||||||
|
/// 回填 GAO 产品名称拼音搜索字段
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-10
|
||||||
|
public class V78__backfill_gao_product_pinyin_search extends BaseJavaMigration {
|
||||||
|
|
||||||
|
private static final int BATCH_SIZE = 500;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void migrate(Context context) throws Exception {
|
||||||
|
Connection connection = context.getConnection();
|
||||||
|
try (
|
||||||
|
PreparedStatement select = connection.prepareStatement("""
|
||||||
|
SELECT `id`, `name`
|
||||||
|
FROM `gao_product`
|
||||||
|
WHERE `name` IS NOT NULL
|
||||||
|
AND (`name_pinyin_full` IS NULL OR `name_pinyin_initial` IS NULL OR `name_pinyin_mixed` IS NULL)
|
||||||
|
""");
|
||||||
|
PreparedStatement update = connection.prepareStatement("""
|
||||||
|
UPDATE `gao_product`
|
||||||
|
SET `name_pinyin_full` = ?, `name_pinyin_initial` = ?, `name_pinyin_mixed` = ?
|
||||||
|
WHERE `id` = ?
|
||||||
|
""")) {
|
||||||
|
try (ResultSet resultSet = select.executeQuery()) {
|
||||||
|
int batchSize = 0;
|
||||||
|
while (resultSet.next()) {
|
||||||
|
String name = resultSet.getString("name");
|
||||||
|
update.setString(1, PinyinUtil.full(name));
|
||||||
|
update.setString(2, PinyinUtil.initial(name));
|
||||||
|
update.setString(3, PinyinUtil.mixed(name));
|
||||||
|
update.setString(4, resultSet.getString("id"));
|
||||||
|
update.addBatch();
|
||||||
|
batchSize++;
|
||||||
|
if (batchSize >= BATCH_SIZE) {
|
||||||
|
update.executeBatch();
|
||||||
|
batchSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (0 < batchSize) {
|
||||||
|
update.executeBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
-- 积分规则已迁移到 trigger_source_type、trigger_ref_type、trigger_ref_id 模型。
|
||||||
|
-- 删除旧模型字段,避免实体未赋值时触发非空约束。
|
||||||
|
ALTER TABLE `gao_point_rule`
|
||||||
|
DROP KEY `idx_gao_point_rule_match`,
|
||||||
|
DROP KEY `idx_gao_point_rule_type`,
|
||||||
|
DROP COLUMN `event_id`,
|
||||||
|
DROP COLUMN `trigger_type`,
|
||||||
|
DROP COLUMN `point_mode`,
|
||||||
|
DROP COLUMN `condition_json`;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user