Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/yudao-cloud
# Conflicts: # yudao-module-mall/yudao-module-promotion-server/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.javamaster
commit
5a7e5e3ea7
|
|
@ -1,11 +1,18 @@
|
||||||
|
######################################################################
|
||||||
|
# Build Tools
|
||||||
|
|
||||||
# 查看更多 .gitignore 配置 -> https://help.github.com/articles/ignoring-files/
|
.gradle
|
||||||
|
/build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
target/
|
target/
|
||||||
!.mvn/wrapper/maven-wrapper.jar
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
.flattened-pom.xml
|
.flattened-pom.xml
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# IDE
|
||||||
|
|
||||||
### STS ###
|
### STS ###
|
||||||
.apt_generated
|
.apt_generated
|
||||||
.classpath
|
.classpath
|
||||||
|
|
@ -13,64 +20,36 @@ target/
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
.springBeans
|
.springBeans
|
||||||
.sts4-cache
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
### IntelliJ IDEA ###
|
||||||
.idea
|
.idea
|
||||||
*.iws
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.class
|
|
||||||
target/*
|
|
||||||
|
|
||||||
### NetBeans ###
|
### NetBeans ###
|
||||||
/nbproject/private/
|
nbproject/private/
|
||||||
/nbbuild/
|
build/*
|
||||||
/dist/
|
nbbuild/
|
||||||
/nbdist/
|
dist/
|
||||||
/.nb-gradle/
|
nbdist/
|
||||||
/build/
|
.nb-gradle/
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Others
|
||||||
### admin-web ###
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
**/node_modules
|
|
||||||
|
|
||||||
# roadhog-api-doc ignore
|
|
||||||
/src/utils/request-temp.js
|
|
||||||
_roadhog-api-doc
|
|
||||||
|
|
||||||
# production
|
|
||||||
/dist
|
|
||||||
/.vscode
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-error.log
|
|
||||||
|
|
||||||
/coverage
|
|
||||||
.idea
|
|
||||||
yarn.lock
|
|
||||||
package-lock.json
|
|
||||||
*bak
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# visual studio code
|
|
||||||
.history
|
|
||||||
*.log
|
*.log
|
||||||
|
*.xml.versionsBackup
|
||||||
|
*.swp
|
||||||
|
|
||||||
functions/mock
|
!*/build/*.java
|
||||||
.temp/**
|
!*/build/*.html
|
||||||
|
!*/build/*.xml
|
||||||
|
|
||||||
# umi
|
### JRebel ###
|
||||||
.umi
|
rebel.xml
|
||||||
.umi-production
|
|
||||||
|
|
||||||
# screenshot
|
application-my.yaml
|
||||||
screenshot
|
|
||||||
.firebase
|
/yudao-ui-app/unpackage/
|
||||||
sessionStore
|
.DS_Store
|
||||||
outputs/
|
**/.DS_Store
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -57,10 +57,12 @@ docker load -i dm8_20240715_x86_rh6_rq_single.tar
|
||||||
```Bash
|
```Bash
|
||||||
docker compose up -d dm8
|
docker compose up -d dm8
|
||||||
# 注意:启动完 dm 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本
|
# 注意:启动完 dm 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本
|
||||||
docker compose exec dm8 bash -c '/opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql'
|
docker compose exec dm8 bash -c 'printf "SET DEFINE OFF;\n" > /tmp/schema-with-define-off.sql && cat /tmp/schema.sql >> /tmp/schema-with-define-off.sql && /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema-with-define-off.sql'
|
||||||
exit
|
exit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 注意:项目 DM8 脚本使用 `varchar(n char)` 保持和 MySQL `varchar(n)` 一致的字符长度语义,建议初始化 DM8 时使用 `PAGE_SIZE=16`、`UNICODE_FLAG=1`。`sql/tools/docker-compose.yaml` 已按该配置提供示例。使用 `disql` 导入时需要先执行 `SET DEFINE OFF;`,避免数据里的 `&` 被当作变量替换。
|
||||||
|
|
||||||
### 1.6 KingbaseES 人大金仓
|
### 1.6 KingbaseES 人大金仓
|
||||||
|
|
||||||
① 下载人大金仓 Docker 镜像:
|
① 下载人大金仓 Docker 镜像:
|
||||||
|
|
|
||||||
|
|
@ -871,7 +871,9 @@ class DM8Convertor(Convertor):
|
||||||
type = type.lower()
|
type = type.lower()
|
||||||
|
|
||||||
if type == "varchar":
|
if type == "varchar":
|
||||||
return f"varchar({size})"
|
# MySQL varchar(n) is character-oriented. DM8 may treat varchar(n)
|
||||||
|
# as bytes, so use explicit CHAR semantics for generated scripts.
|
||||||
|
return f"varchar({size} char)"
|
||||||
if type in ("int", "int unsigned"):
|
if type in ("int", "int unsigned"):
|
||||||
return "int"
|
return "int"
|
||||||
if type in ("bigint", "bigint unsigned"):
|
if type in ("bigint", "bigint unsigned"):
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.common.util.date;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.date.DatePattern;
|
import cn.hutool.core.date.DatePattern;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
import cn.hutool.core.date.TemporalAccessorUtil;
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
||||||
|
|
@ -16,6 +15,7 @@ import java.time.temporal.ChronoUnit;
|
||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import static cn.hutool.core.date.DatePattern.*;
|
import static cn.hutool.core.date.DatePattern.*;
|
||||||
|
|
||||||
|
|
@ -33,6 +33,11 @@ public class LocalDateTimeUtils {
|
||||||
|
|
||||||
public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);
|
public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认时区
|
||||||
|
*/
|
||||||
|
private static final ZoneId DEFAULT_ZONE_ID = TimeZone.getTimeZone(DateUtils.TIME_ZONE_DEFAULT).toZoneId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析时间
|
* 解析时间
|
||||||
*
|
*
|
||||||
|
|
@ -65,6 +70,27 @@ public class LocalDateTimeUtils {
|
||||||
return date.isAfter(LocalDateTime.now());
|
return date.isAfter(LocalDateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Unix 秒时间戳转换为默认时区的本地时间
|
||||||
|
*
|
||||||
|
* @param epochSecond Unix 秒时间戳
|
||||||
|
* @return 本地时间
|
||||||
|
*/
|
||||||
|
public static LocalDateTime ofEpochSecond(long epochSecond) {
|
||||||
|
return ofEpochSecond(epochSecond, DEFAULT_ZONE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Unix 秒时间戳转换为指定时区的本地时间
|
||||||
|
*
|
||||||
|
* @param epochSecond Unix 秒时间戳
|
||||||
|
* @param zoneId 时区编号
|
||||||
|
* @return 本地时间
|
||||||
|
*/
|
||||||
|
public static LocalDateTime ofEpochSecond(long epochSecond, ZoneId zoneId) {
|
||||||
|
return LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), zoneId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建指定时间
|
* 创建指定时间
|
||||||
*
|
*
|
||||||
|
|
@ -397,7 +423,18 @@ public class LocalDateTimeUtils {
|
||||||
* @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常
|
* @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常
|
||||||
*/
|
*/
|
||||||
public static Long toEpochSecond(LocalDateTime sourceDateTime) {
|
public static Long toEpochSecond(LocalDateTime sourceDateTime) {
|
||||||
return TemporalAccessorUtil.toInstant(sourceDateTime).getEpochSecond();
|
return toEpochSecond(sourceDateTime, DEFAULT_ZONE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将给定的 {@link LocalDateTime} 按指定时区转换为自 Unix 纪元时间(1970-01-01T00:00:00Z)以来的秒数。
|
||||||
|
*
|
||||||
|
* @param sourceDateTime 需要转换的本地日期时间,不能为空
|
||||||
|
* @param zoneId 时区编号
|
||||||
|
* @return 自 1970-01-01T00:00:00Z 起的秒数(epoch second)
|
||||||
|
*/
|
||||||
|
public static Long toEpochSecond(LocalDateTime sourceDateTime, ZoneId zoneId) {
|
||||||
|
return sourceDateTime.atZone(zoneId).toEpochSecond();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,7 @@ public class BpmProcessInstanceController {
|
||||||
@GetMapping("/get-bpmn-model-view")
|
@GetMapping("/get-bpmn-model-view")
|
||||||
@Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用")
|
@Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用")
|
||||||
@Parameter(name = "id", description = "流程实例的编号", required = true)
|
@Parameter(name = "id", description = "流程实例的编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
|
||||||
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(
|
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(
|
||||||
@RequestParam(value = "id") String id) {
|
@RequestParam(value = "id") String id) {
|
||||||
return success(processInstanceService.getProcessInstanceBpmnModelView(id));
|
return success(processInstanceService.getProcessInstanceBpmnModelView(id));
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable<Integer> {
|
||||||
MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"),
|
MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"),
|
||||||
POST(22, "岗位"),
|
POST(22, "岗位"),
|
||||||
USER(30, "用户"),
|
USER(30, "用户"),
|
||||||
APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人
|
APPROVE_USER_SELECT(34, "审批人自选"), // 当前审批人,可在审批时,选择下一个节点的审批人
|
||||||
START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人
|
START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人
|
||||||
START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景
|
START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景
|
||||||
START_USER_DEPT_LEADER(37, "发起人部门负责人"),
|
START_USER_DEPT_LEADER(37, "发起人部门负责人"),
|
||||||
|
|
|
||||||
|
|
@ -958,13 +958,88 @@ public class BpmnModelUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找起始节点下一个用户任务列表列表
|
* 查找起始节点下一个用户任务列表, 该方法会递归向下查找:跳过非 UserTask 节点, 支持网关条件判断
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>排他网关:走满足条件的唯一一条路径(含默认路径兜底)</li>
|
||||||
|
* <li>包容网关:走所有满足条件的路径</li>
|
||||||
|
* <li>并行网关:走所有出口路径</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param currentElement 当前节点
|
||||||
|
* @param bpmnModel BPMN 模型
|
||||||
|
* @param variables 流程变量(用于网关条件判断)
|
||||||
|
* @return 下一个用户任务节点列表
|
||||||
|
*/
|
||||||
|
public static List<UserTask> getNextUserTasks(FlowElement currentElement, BpmnModel bpmnModel,
|
||||||
|
Map<String, Object> variables) {
|
||||||
|
return getNextUserTasks(currentElement, bpmnModel, variables, new HashSet<>(), new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<UserTask> getNextUserTasks(FlowElement currentElement, BpmnModel bpmnModel,
|
||||||
|
Map<String, Object> variables,
|
||||||
|
Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||||
|
// 1. 根据节点类型决定要遍历的出口连线
|
||||||
|
// 网关需要根据条件表达式筛选;其它节点直接取所有 outgoing flows
|
||||||
|
List<SequenceFlow> outgoingFlows;
|
||||||
|
if (currentElement instanceof Gateway) {
|
||||||
|
outgoingFlows = getGatewayOutgoingFlows((Gateway) currentElement, variables);
|
||||||
|
} else {
|
||||||
|
outgoingFlows = getElementOutgoingFlows(currentElement);
|
||||||
|
}
|
||||||
|
if (CollUtil.isEmpty(outgoingFlows)) {
|
||||||
|
return userTaskList;
|
||||||
|
}
|
||||||
|
// 2. 遍历出口连线,递归查找用户任务
|
||||||
|
for (SequenceFlow outgoingFlow : outgoingFlows) {
|
||||||
|
// 防止连线成环导致死循环
|
||||||
|
if (hasSequenceFlow.contains(outgoingFlow.getId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hasSequenceFlow.add(outgoingFlow.getId());
|
||||||
|
// 获取目标节点
|
||||||
|
FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef());
|
||||||
|
if (targetElement == null || targetElement instanceof EndEvent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (targetElement instanceof UserTask) {
|
||||||
|
// 找到用户任务:加入结果
|
||||||
|
userTaskList.add((UserTask) targetElement);
|
||||||
|
} else {
|
||||||
|
// 非用户任务(网关、服务任务、中间事件等):继续递归向下查找
|
||||||
|
getNextUserTasks(targetElement, bpmnModel, variables, hasSequenceFlow, userTaskList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userTaskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网关类型与流程变量,筛选网关的出口连线
|
||||||
|
*
|
||||||
|
* @param gateway 网关节点
|
||||||
|
* @param variables 流程变量
|
||||||
|
* @return 命中的出口连线列表
|
||||||
|
*/
|
||||||
|
private static List<SequenceFlow> getGatewayOutgoingFlows(Gateway gateway, Map<String, Object> variables) {
|
||||||
|
if (gateway instanceof ExclusiveGateway) {
|
||||||
|
SequenceFlow matchFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables);
|
||||||
|
return matchFlow == null ? Collections.emptyList() : Collections.singletonList(matchFlow);
|
||||||
|
}
|
||||||
|
if (gateway instanceof InclusiveGateway) {
|
||||||
|
return new ArrayList<>(findMatchSequenceFlowsByInclusiveGateway(gateway, variables));
|
||||||
|
}
|
||||||
|
// 默认(并行网关等):走所有出口
|
||||||
|
return gateway.getOutgoingFlows();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找起始节点下一个用户任务列表列表, 不判断网关条件
|
||||||
*
|
*
|
||||||
* @param source 起始节点
|
* @param source 起始节点
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public static List<UserTask> getNextUserTasks(FlowElement source) {
|
public static List<UserTask> getNextUserTasks(FlowElement source) {
|
||||||
return getNextUserTasks(source, null, null);
|
return getNextUserTasks(source, new HashSet<>(), new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.*;
|
import cn.hutool.core.util.*;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||||
|
|
@ -277,11 +276,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||||
processVariables.putAll(reqVO.getProcessVariables());
|
processVariables.putAll(reqVO.getProcessVariables());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 获取下一个将要执行的节点集合
|
// 3.1 获取下一个将要执行的节点集合
|
||||||
FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
|
FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
|
||||||
List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);
|
// 3.2 获取 UserTask 节点
|
||||||
// 仅仅获取 UserTask 节点 TODO add from jason:如果网关节点和网关节点相连,获取下个 UserTask. 貌似有点不准。
|
List<UserTask> nextUserTaskList = BpmnModelUtils.getNextUserTasks(flowElement, bpmnModel, processVariables);
|
||||||
List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask);
|
|
||||||
List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())
|
List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())
|
||||||
.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
|
.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
|
||||||
.setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
|
.setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
|
||||||
|
|
|
||||||
|
|
@ -669,10 +669,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
// 1. 获取下一个将要执行的节点集合
|
// 1. 获取下一个将要执行的节点集合
|
||||||
FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
|
FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
|
||||||
List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
|
List<UserTask> nextFlowNodes = getNextUserTasks(flowElement, bpmnModel, variables);
|
||||||
|
|
||||||
// 2. 校验选择的下一个节点的审批人,是否合法
|
// 2. 校验选择的下一个节点的审批人,是否合法
|
||||||
for (FlowNode nextFlowNode : nextFlowNodes) {
|
for (UserTask nextFlowNode : nextFlowNodes) {
|
||||||
Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
|
Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
|
||||||
// 2.1 情况一:如果节点中的审批人策略为 发起人自选
|
// 2.1 情况一:如果节点中的审批人策略为 发起人自选
|
||||||
if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
|
if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
|
||||||
|
|
@ -698,8 +698,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
|
|
||||||
// 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空
|
// 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空
|
||||||
if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
|
if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
|
||||||
// 如果节点存在,但未配置审批人
|
// 特殊:如果当前节点已经存在审批人,则不允许覆盖。 例如并行节点后,设置的审批人自选节点。 https://t.zsxq.com/daxv1
|
||||||
Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
|
Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
|
||||||
|
if (approveUserSelectAssignees != null && CollUtil.isNotEmpty(approveUserSelectAssignees.get(nextFlowNode.getId()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 如果节点存在,但未配置审批人
|
||||||
List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;
|
List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;
|
||||||
if (CollUtil.isEmpty(assignees)) {
|
if (CollUtil.isEmpty(assignees)) {
|
||||||
throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
|
throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ public interface ErrorCodeConstants {
|
||||||
ErrorCode BUSINESS_DELETE_FAIL_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, "商机已关联合同,不能删除");
|
ErrorCode BUSINESS_DELETE_FAIL_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, "商机已关联合同,不能删除");
|
||||||
ErrorCode BUSINESS_UPDATE_STATUS_FAIL_END_STATUS = new ErrorCode(1_020_002_002, "更新商机状态失败,原因:已经是结束状态");
|
ErrorCode BUSINESS_UPDATE_STATUS_FAIL_END_STATUS = new ErrorCode(1_020_002_002, "更新商机状态失败,原因:已经是结束状态");
|
||||||
ErrorCode BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS = new ErrorCode(1_020_002_003, "更新商机状态失败,原因:已经是该状态");
|
ErrorCode BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS = new ErrorCode(1_020_002_003, "更新商机状态失败,原因:已经是该状态");
|
||||||
|
ErrorCode BUSINESS_CONTACT_CUSTOMER_NOT_MATCH = new ErrorCode(1_020_002_004, "商机关联的联系人不属于该客户");
|
||||||
|
|
||||||
// ========== 联系人管理 1-020-003-000 ==========
|
// ========== 联系人管理 1-020-003-000 ==========
|
||||||
ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
|
ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,26 @@ public class CrmBusinessController {
|
||||||
.setCustomerId(business.getCustomerId())));
|
.setCustomerId(business.getCustomerId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/list-by-customer")
|
||||||
|
@Operation(summary = "获得商机列表,基于指定客户")
|
||||||
|
@Parameter(name = "customerId", description = "客户编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||||
|
public CommonResult<List<CrmBusinessRespVO>> getBusinessListByCustomer(@RequestParam("customerId") Long customerId) {
|
||||||
|
List<CrmBusinessDO> list = businessService.getBusinessListByCustomerId(customerId);
|
||||||
|
return success(convertList(list, business -> // 只返回 id、name 字段
|
||||||
|
new CrmBusinessRespVO().setId(business.getId()).setName(business.getName())
|
||||||
|
.setCustomerId(business.getCustomerId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/list-by-contact")
|
||||||
|
@Operation(summary = "获得商机列表,基于指定联系人")
|
||||||
|
@Parameter(name = "contactId", description = "联系人编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||||
|
public CommonResult<List<CrmBusinessRespVO>> getBusinessListByContact(@RequestParam("contactId") Long contactId) {
|
||||||
|
List<CrmBusinessDO> list = businessService.getBusinessListByContact(contactId);
|
||||||
|
return success(buildBusinessDetailList(list));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获得商机分页")
|
@Operation(summary = "获得商机分页")
|
||||||
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,17 @@ public class CrmContactController {
|
||||||
.setCustomerId(contact.getCustomerId())));
|
.setCustomerId(contact.getCustomerId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/list-by-customer")
|
||||||
|
@Operation(summary = "获得联系人列表,基于指定客户")
|
||||||
|
@Parameter(name = "customerId", description = "客户编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||||
|
public CommonResult<List<CrmContactRespVO>> getContactListByCustomer(@RequestParam("customerId") Long customerId) {
|
||||||
|
List<CrmContactDO> list = contactService.getContactListByCustomerId(customerId);
|
||||||
|
return success(convertList(list, contact -> // 只返回 id、name 字段
|
||||||
|
new CrmContactRespVO().setId(contact.getId()).setName(contact.getName())
|
||||||
|
.setCustomerId(contact.getCustomerId())));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获得联系人分页")
|
@Operation(summary = "获得联系人分页")
|
||||||
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,12 @@ import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecor
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
|
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
||||||
import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
|
import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
|
@ -27,10 +30,13 @@ import javax.validation.Valid;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED;
|
||||||
|
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
|
||||||
|
|
||||||
|
|
||||||
@Tag(name = "管理后台 - 跟进记录")
|
@Tag(name = "管理后台 - 跟进记录")
|
||||||
|
|
@ -45,6 +51,8 @@ public class CrmFollowUpRecordController {
|
||||||
private CrmContactService contactService;
|
private CrmContactService contactService;
|
||||||
@Resource
|
@Resource
|
||||||
private CrmBusinessService businessService;
|
private CrmBusinessService businessService;
|
||||||
|
@Resource
|
||||||
|
private CrmPermissionService permissionService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AdminUserApi adminUserApi;
|
private AdminUserApi adminUserApi;
|
||||||
|
|
@ -68,6 +76,13 @@ public class CrmFollowUpRecordController {
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam("id") Long id) {
|
public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam("id") Long id) {
|
||||||
CrmFollowUpRecordDO followUpRecord = followUpRecordService.getFollowUpRecord(id);
|
CrmFollowUpRecordDO followUpRecord = followUpRecordService.getFollowUpRecord(id);
|
||||||
|
if (followUpRecord == null) {
|
||||||
|
throw exception(FOLLOW_UP_RECORD_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
if (!permissionService.hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(),
|
||||||
|
getLoginUserId(), CrmPermissionLevelEnum.READ)) {
|
||||||
|
throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(followUpRecord.getBizType()));
|
||||||
|
}
|
||||||
return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));
|
return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,10 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
|
||||||
.eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
|
.eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default List<CrmBusinessDO> selectListByCustomerId(Long customerId) {
|
||||||
|
return selectList(CrmBusinessDO::getCustomerId, customerId);
|
||||||
|
}
|
||||||
|
|
||||||
default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {
|
default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {
|
||||||
return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()
|
return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||||
.in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())
|
.in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,22 @@ public interface CrmBusinessService {
|
||||||
*/
|
*/
|
||||||
List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商机列表,基于指定客户
|
||||||
|
*
|
||||||
|
* @param customerId 客户编号
|
||||||
|
* @return 商机列表
|
||||||
|
*/
|
||||||
|
List<CrmBusinessDO> getBusinessListByCustomerId(Long customerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商机列表,基于指定联系人
|
||||||
|
*
|
||||||
|
* @param contactId 联系人编号
|
||||||
|
* @return 商机列表
|
||||||
|
*/
|
||||||
|
List<CrmBusinessDO> getBusinessListByContact(Long contactId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得商机分页,目前用于【数据统计】
|
* 获得商机分页,目前用于【数据统计】
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.business;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.ListUtil;
|
import cn.hutool.core.collection.ListUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
|
@ -15,6 +16,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
|
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
|
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
|
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
|
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
|
||||||
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
|
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
|
||||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||||
|
|
@ -194,9 +196,15 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||||
if (saveReqVO.getCustomerId() != null) {
|
if (saveReqVO.getCustomerId() != null) {
|
||||||
customerService.validateCustomer(saveReqVO.getCustomerId());
|
customerService.validateCustomer(saveReqVO.getCustomerId());
|
||||||
}
|
}
|
||||||
// 校验联系人
|
// 校验联系人(且必须与商机属于同一客户,避免跨客户关联)
|
||||||
if (saveReqVO.getContactId() != null) {
|
if (saveReqVO.getContactId() != null) {
|
||||||
contactService.validateContact(saveReqVO.getContactId());
|
contactService.validateContact(saveReqVO.getContactId());
|
||||||
|
if (saveReqVO.getCustomerId() != null) {
|
||||||
|
CrmContactDO contact = contactService.getContact(saveReqVO.getContactId());
|
||||||
|
if (ObjUtil.notEqual(saveReqVO.getCustomerId(), contact.getCustomerId())) {
|
||||||
|
throw exception(BUSINESS_CONTACT_CUSTOMER_NOT_MATCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 校验负责人
|
// 校验负责人
|
||||||
if (saveReqVO.getOwnerUserId() != null) {
|
if (saveReqVO.getOwnerUserId() != null) {
|
||||||
|
|
@ -377,6 +385,24 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||||
return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#customerId", level = CrmPermissionLevelEnum.READ)
|
||||||
|
public List<CrmBusinessDO> getBusinessListByCustomerId(Long customerId) {
|
||||||
|
return businessMapper.selectListByCustomerId(customerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.READ)
|
||||||
|
public List<CrmBusinessDO> getBusinessListByContact(Long contactId) {
|
||||||
|
// 1. 查询关联的商机编号
|
||||||
|
List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByContactId(contactId);
|
||||||
|
if (CollUtil.isEmpty(contactBusinessList)) {
|
||||||
|
return ListUtil.empty();
|
||||||
|
}
|
||||||
|
// 2. 查询商机列表
|
||||||
|
return businessMapper.selectByIds(convertSet(contactBusinessList, CrmContactBusinessDO::getBusinessId));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {
|
public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {
|
||||||
return businessMapper.selectPage(pageVO);
|
return businessMapper.selectPage(pageVO);
|
||||||
|
|
|
||||||
|
|
@ -169,4 +169,12 @@ public interface CrmContactService {
|
||||||
*/
|
*/
|
||||||
List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得联系人列表,基于指定客户
|
||||||
|
*
|
||||||
|
* @param customerId 客户编号
|
||||||
|
* @return 联系人列表
|
||||||
|
*/
|
||||||
|
List<CrmContactDO> getContactListByCustomerId(Long customerId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -304,4 +304,10 @@ public class CrmContactServiceImpl implements CrmContactService {
|
||||||
return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#customerId", level = CrmPermissionLevelEnum.READ)
|
||||||
|
public List<CrmContactDO> getContactListByCustomerId(Long customerId) {
|
||||||
|
return contactMapper.selectListByCustomerId(customerId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,14 @@ public class ErpSaleOrderController {
|
||||||
|
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@Operation(summary = "创建销售订单")
|
@Operation(summary = "创建销售订单")
|
||||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:create')")
|
@PreAuthorize("@ss.hasPermission('erp:sale-order:create')")
|
||||||
public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) {
|
public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) {
|
||||||
return success(saleOrderService.createSaleOrder(createReqVO));
|
return success(saleOrderService.createSaleOrder(createReqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
@Operation(summary = "更新销售订单")
|
@Operation(summary = "更新销售订单")
|
||||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:update')")
|
@PreAuthorize("@ss.hasPermission('erp:sale-order:update')")
|
||||||
public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) {
|
public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) {
|
||||||
saleOrderService.updateSaleOrder(updateReqVO);
|
saleOrderService.updateSaleOrder(updateReqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
|
|
@ -76,7 +76,7 @@ public class ErpSaleOrderController {
|
||||||
|
|
||||||
@PutMapping("/update-status")
|
@PutMapping("/update-status")
|
||||||
@Operation(summary = "更新销售订单的状态")
|
@Operation(summary = "更新销售订单的状态")
|
||||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:update-status')")
|
@PreAuthorize("@ss.hasPermission('erp:sale-order:update-status')")
|
||||||
public CommonResult<Boolean> updateSaleOrderStatus(@RequestParam("id") Long id,
|
public CommonResult<Boolean> updateSaleOrderStatus(@RequestParam("id") Long id,
|
||||||
@RequestParam("status") Integer status) {
|
@RequestParam("status") Integer status) {
|
||||||
saleOrderService.updateSaleOrderStatus(id, status);
|
saleOrderService.updateSaleOrderStatus(id, status);
|
||||||
|
|
@ -86,7 +86,7 @@ public class ErpSaleOrderController {
|
||||||
@DeleteMapping("/delete")
|
@DeleteMapping("/delete")
|
||||||
@Operation(summary = "删除销售订单")
|
@Operation(summary = "删除销售订单")
|
||||||
@Parameter(name = "ids", description = "编号数组", required = true)
|
@Parameter(name = "ids", description = "编号数组", required = true)
|
||||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:delete')")
|
@PreAuthorize("@ss.hasPermission('erp:sale-order:delete')")
|
||||||
public CommonResult<Boolean> deleteSaleOrder(@RequestParam("ids") List<Long> ids) {
|
public CommonResult<Boolean> deleteSaleOrder(@RequestParam("ids") List<Long> ids) {
|
||||||
saleOrderService.deleteSaleOrder(ids);
|
saleOrderService.deleteSaleOrder(ids);
|
||||||
return success(true);
|
return success(true);
|
||||||
|
|
@ -95,7 +95,7 @@ public class ErpSaleOrderController {
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "获得销售订单")
|
@Operation(summary = "获得销售订单")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:query')")
|
@PreAuthorize("@ss.hasPermission('erp:sale-order:query')")
|
||||||
public CommonResult<ErpSaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) {
|
public CommonResult<ErpSaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) {
|
||||||
ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
|
ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
|
||||||
if (saleOrder == null) {
|
if (saleOrder == null) {
|
||||||
|
|
@ -115,7 +115,7 @@ public class ErpSaleOrderController {
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获得销售订单分页")
|
@Operation(summary = "获得销售订单分页")
|
||||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:query')")
|
@PreAuthorize("@ss.hasPermission('erp:sale-order:query')")
|
||||||
public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) {
|
public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) {
|
||||||
PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
|
PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
|
||||||
return success(buildSaleOrderVOPageResult(pageResult));
|
return success(buildSaleOrderVOPageResult(pageResult));
|
||||||
|
|
@ -123,7 +123,7 @@ public class ErpSaleOrderController {
|
||||||
|
|
||||||
@GetMapping("/export-excel")
|
@GetMapping("/export-excel")
|
||||||
@Operation(summary = "导出销售订单 Excel")
|
@Operation(summary = "导出销售订单 Excel")
|
||||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:export')")
|
@PreAuthorize("@ss.hasPermission('erp:sale-order:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO,
|
public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO,
|
||||||
HttpServletResponse response) throws IOException {
|
HttpServletResponse response) throws IOException {
|
||||||
|
|
@ -161,4 +161,4 @@ public class ErpSaleOrderController {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,49 +40,13 @@
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
#elseif ($column.htmlType == "datetime" && $listOperationCondition == "BETWEEN")
|
#elseif ($column.htmlType == "datetime" && $listOperationCondition == "BETWEEN")
|
||||||
#set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})
|
<yd-search-date-range v-model="formData.${javaField}" label="${comment}" />
|
||||||
<view class="yd-search-form-item">
|
|
||||||
<view class="yd-search-form-label">
|
|
||||||
${comment}
|
|
||||||
</view>
|
|
||||||
<view class="yd-search-form-date-range-container">
|
|
||||||
<view @click="visible${AttrName}[0] = true">
|
|
||||||
<view class="yd-search-form-date-range-picker">
|
|
||||||
{{ formatDate(formData.${javaField}?.[0]) || '开始日期' }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
-
|
|
||||||
<view @click="visible${AttrName}[1] = true">
|
|
||||||
<view class="yd-search-form-date-range-picker">
|
|
||||||
{{ formatDate(formData.${javaField}?.[1]) || '结束日期' }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<wd-datetime-picker-view v-if="visible${AttrName}[0]" v-model="temp${AttrName}[0]" type="date" />
|
|
||||||
<view v-if="visible${AttrName}[0]" class="yd-search-form-date-range-actions">
|
|
||||||
<wd-button size="small" plain @click="visible${AttrName}[0] = false">
|
|
||||||
取消
|
|
||||||
</wd-button>
|
|
||||||
<wd-button size="small" type="primary" @click="handle${AttrName}0Confirm">
|
|
||||||
确定
|
|
||||||
</wd-button>
|
|
||||||
</view>
|
|
||||||
<wd-datetime-picker-view v-if="visible${AttrName}[1]" v-model="temp${AttrName}[1]" type="date" />
|
|
||||||
<view v-if="visible${AttrName}[1]" class="yd-search-form-date-range-actions">
|
|
||||||
<wd-button size="small" plain @click="visible${AttrName}[1] = false">
|
|
||||||
取消
|
|
||||||
</wd-button>
|
|
||||||
<wd-button size="small" type="primary" @click="handle${AttrName}1Confirm">
|
|
||||||
确定
|
|
||||||
</wd-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
#elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
|
#elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
|
||||||
<view class="yd-search-form-item">
|
<view class="yd-search-form-item">
|
||||||
<view class="yd-search-form-label">
|
<view class="yd-search-form-label">
|
||||||
${comment}
|
${comment}
|
||||||
</view>
|
</view>
|
||||||
<wd-radio-group v-model="formData.${javaField}" shape="button">
|
<wd-radio-group v-model="formData.${javaField}" type="button">
|
||||||
<wd-radio :value="-1">
|
<wd-radio :value="-1">
|
||||||
全部
|
全部
|
||||||
</wd-radio>
|
</wd-radio>
|
||||||
|
|
@ -236,29 +200,6 @@ const placeholder = computed(() => {
|
||||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索${table.classComment}'
|
return conditions.length > 0 ? conditions.join(' | ') : '搜索${table.classComment}'
|
||||||
})
|
})
|
||||||
|
|
||||||
#if ($hasDateTimeBetween == 1)
|
|
||||||
#foreach($column in $columns)
|
|
||||||
#if ($column.listOperation && $column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
|
|
||||||
#set ($javaField = $column.javaField)
|
|
||||||
#set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})
|
|
||||||
const visible${AttrName} = ref<[boolean, boolean]>([false, false]) // ${column.columnComment}选择器状态
|
|
||||||
const temp${AttrName} = ref<[number, number]>([Date.now(), Date.now()]) // ${column.columnComment}临时值
|
|
||||||
|
|
||||||
/** 确认${column.columnComment}开始日期 */
|
|
||||||
function handle${AttrName}0Confirm() {
|
|
||||||
formData.${javaField} = [temp${AttrName}.value[0], formData.${javaField}?.[1]]
|
|
||||||
visible${AttrName}.value[0] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 确认${column.columnComment}结束日期 */
|
|
||||||
function handle${AttrName}1Confirm() {
|
|
||||||
formData.${javaField} = [formData.${javaField}?.[0], temp${AttrName}.value[1]]
|
|
||||||
visible${AttrName}.value[1] = false
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,10 @@
|
||||||
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
||||||
import { onUnload } from '@dcloudio/uni-app'
|
import { onUnload } from '@dcloudio/uni-app'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useToast } from 'wot-design-uni'
|
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||||
import { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
import { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
||||||
import { useAccess } from '@/hooks/useAccess'
|
import { useAccess } from '@/hooks/useAccess'
|
||||||
import { navigateBackPlus } from '@/utils'
|
import { delay, navigateBackPlus } from '@/utils'
|
||||||
#if ($hasDict == 1)
|
#if ($hasDict == 1)
|
||||||
import { DICT_TYPE } from '@/utils/constants'
|
import { DICT_TYPE } from '@/utils/constants'
|
||||||
#end
|
#end
|
||||||
|
|
@ -133,9 +133,7 @@ function handleDelete() {
|
||||||
await delete${simpleClassName}(props.id)
|
await delete${simpleClassName}(props.id)
|
||||||
toast.success('删除成功')
|
toast.success('删除成功')
|
||||||
uni.$emit('${table.moduleName}:${table.businessName}:reload')
|
uni.$emit('${table.moduleName}:${table.businessName}:reload')
|
||||||
setTimeout(() => {
|
delay(handleBack)
|
||||||
handleBack()
|
|
||||||
}, 500)
|
|
||||||
} finally {
|
} finally {
|
||||||
deleting.value = false
|
deleting.value = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<!-- 表单区域 -->
|
<!-- 表单区域 -->
|
||||||
<view>
|
<view>
|
||||||
<wd-form ref="formRef" :model="formData" :rules="formRules">
|
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||||
<wd-cell-group border>
|
<wd-cell-group border>
|
||||||
#foreach($column in $columns)
|
#foreach($column in $columns)
|
||||||
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey)
|
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey)
|
||||||
|
|
@ -25,10 +25,23 @@
|
||||||
#elseif ($javaType == "Boolean")
|
#elseif ($javaType == "Boolean")
|
||||||
#set ($dictMethod = "getBoolDictOptions")
|
#set ($dictMethod = "getBoolDictOptions")
|
||||||
#end
|
#end
|
||||||
## 优先判断是否有字典,有字典则使用 radio-group
|
## 下拉选择(字典)→ yd-form-picker,内部按 dictType 解析选项,适合选项较多
|
||||||
#if (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
|
#if ($column.htmlType == "select" && $dictType && "" != $dictType)
|
||||||
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
|
<yd-form-picker
|
||||||
<wd-radio-group v-model="formData.${javaField}" shape="button">
|
v-model="formData.${javaField}"
|
||||||
|
label="${comment}"
|
||||||
|
label-width="180rpx"
|
||||||
|
prop="${javaField}"
|
||||||
|
:dict-type="DICT_TYPE.${dictType.toUpperCase()}"
|
||||||
|
#if ($javaType == "String")
|
||||||
|
dict-kind="str"
|
||||||
|
#end
|
||||||
|
placeholder="请选择${comment}"
|
||||||
|
/>
|
||||||
|
## 单选(字典)→ radio-group,适合选项较少
|
||||||
|
#elseif ($column.htmlType == "radio" && $dictType && "" != $dictType)
|
||||||
|
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}" center>
|
||||||
|
<wd-radio-group v-model="formData.${javaField}" type="button">
|
||||||
<wd-radio
|
<wd-radio
|
||||||
v-for="dict in $dictMethod(DICT_TYPE.${dictType.toUpperCase()})"
|
v-for="dict in $dictMethod(DICT_TYPE.${dictType.toUpperCase()})"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
|
|
@ -37,54 +50,78 @@
|
||||||
{{ dict.label }}
|
{{ dict.label }}
|
||||||
</wd-radio>
|
</wd-radio>
|
||||||
</wd-radio-group>
|
</wd-radio-group>
|
||||||
</wd-cell>
|
</wd-form-item>
|
||||||
## 数字类型(无字典)
|
## 数字类型(无字典)
|
||||||
#elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte")
|
#elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte")
|
||||||
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
|
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||||
<wd-input-number
|
<wd-input-number
|
||||||
v-model="formData.${javaField}"
|
v-model="formData.${javaField}"
|
||||||
|
#if (${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal")
|
||||||
|
:precision="2"
|
||||||
|
input-type="number"
|
||||||
|
#end
|
||||||
:min="0"
|
:min="0"
|
||||||
/>
|
/>
|
||||||
</wd-cell>
|
</wd-form-item>
|
||||||
## 布尔类型
|
## 布尔类型
|
||||||
#elseif (${javaType.toLowerCase()} == "boolean")
|
#elseif (${javaType.toLowerCase()} == "boolean")
|
||||||
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
|
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}" center>
|
||||||
<wd-switch v-model="formData.${javaField}" />
|
<wd-switch v-model="formData.${javaField}" />
|
||||||
</wd-cell>
|
</wd-form-item>
|
||||||
## 日期时间类型
|
## 日期时间类型
|
||||||
#elseif (${javaType.toLowerCase()} == "date" || ${javaType.toLowerCase()} == "localdate" || ${javaType.toLowerCase()} == "localdatetime")
|
#elseif (${javaType.toLowerCase()} == "date" || ${javaType.toLowerCase()} == "localdate" || ${javaType.toLowerCase()} == "localdatetime")
|
||||||
#set ($pickerType = "date")
|
#set ($pickerType = "date")
|
||||||
#if (${javaType.toLowerCase()} == "localdatetime")
|
#if (${javaType.toLowerCase()} == "localdatetime")
|
||||||
#set ($pickerType = "datetime")
|
#set ($pickerType = "datetime")
|
||||||
#end
|
#end
|
||||||
|
<wd-form-item
|
||||||
|
title="${comment}"
|
||||||
|
title-width="180rpx"
|
||||||
|
prop="${javaField}"
|
||||||
|
is-link
|
||||||
|
#if ($pickerType == "datetime")
|
||||||
|
:value="formatDateTime(formData.${javaField})"
|
||||||
|
#else
|
||||||
|
:value="formatDate(formData.${javaField})"
|
||||||
|
#end
|
||||||
|
placeholder="请选择${comment}"
|
||||||
|
@click="${javaField}Visible = true"
|
||||||
|
/>
|
||||||
<wd-datetime-picker
|
<wd-datetime-picker
|
||||||
v-model="formData.${javaField}"
|
v-model="formData.${javaField}"
|
||||||
|
v-model:visible="${javaField}Visible"
|
||||||
type="${pickerType}"
|
type="${pickerType}"
|
||||||
label="${comment}"
|
|
||||||
label-width="180rpx"
|
|
||||||
prop="${javaField}"
|
|
||||||
/>
|
/>
|
||||||
## 文本域
|
## 文本域
|
||||||
#elseif ($column.htmlType == "textarea")
|
#elseif ($column.htmlType == "textarea")
|
||||||
<wd-textarea
|
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||||
v-model="formData.${javaField}"
|
<wd-textarea
|
||||||
label="${comment}"
|
v-model="formData.${javaField}"
|
||||||
label-width="180rpx"
|
clearable
|
||||||
placeholder="请输入${comment}"
|
placeholder="请输入${comment}"
|
||||||
:maxlength="200"
|
:maxlength="200"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
clearable
|
/>
|
||||||
/>
|
</wd-form-item>
|
||||||
|
## 图片上传 → yd-upload-img(包 wd-form-item 才能注册到 wd-form 做校验/错误展示)
|
||||||
|
#elseif ($column.htmlType == "imageUpload")
|
||||||
|
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||||
|
<yd-upload-img v-model="formData.${javaField}" directory="${table.moduleName}/${table.businessName}" />
|
||||||
|
</wd-form-item>
|
||||||
|
## 文件上传 → yd-upload-file
|
||||||
|
#elseif ($column.htmlType == "fileUpload")
|
||||||
|
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||||
|
<yd-upload-file v-model="formData.${javaField}" directory="${table.moduleName}/${table.businessName}" />
|
||||||
|
</wd-form-item>
|
||||||
## 默认:文本输入
|
## 默认:文本输入
|
||||||
#else
|
#else
|
||||||
<wd-input
|
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||||
v-model="formData.${javaField}"
|
<wd-input
|
||||||
label="${comment}"
|
v-model="formData.${javaField}"
|
||||||
label-width="180rpx"
|
clearable
|
||||||
prop="${javaField}"
|
placeholder="请输入${comment}"
|
||||||
clearable
|
/>
|
||||||
placeholder="请输入${comment}"
|
</wd-form-item>
|
||||||
/>
|
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
|
|
@ -107,7 +144,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
|
import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
|
||||||
#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
|
#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
|
||||||
#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
|
#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
|
||||||
#set ($primaryTsType = "number")
|
#set ($primaryTsType = "number")
|
||||||
|
|
@ -115,6 +152,8 @@ import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
|
||||||
#set ($primaryTsType = "string")
|
#set ($primaryTsType = "string")
|
||||||
#end
|
#end
|
||||||
#set ($hasDict = 0)
|
#set ($hasDict = 0)
|
||||||
|
#set ($hasDate = 0)
|
||||||
|
#set ($hasDateTime = 0)
|
||||||
#set ($hasGetDictOptions = 0)
|
#set ($hasGetDictOptions = 0)
|
||||||
#set ($hasGetIntDictOptions = 0)
|
#set ($hasGetIntDictOptions = 0)
|
||||||
#set ($hasGetStrDictOptions = 0)
|
#set ($hasGetStrDictOptions = 0)
|
||||||
|
|
@ -124,22 +163,30 @@ import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
|
||||||
&& ($column.htmlType == "select" || $column.htmlType == "radio")
|
&& ($column.htmlType == "select" || $column.htmlType == "radio")
|
||||||
&& $column.dictType && "" != $column.dictType)
|
&& $column.dictType && "" != $column.dictType)
|
||||||
#set ($hasDict = 1)
|
#set ($hasDict = 1)
|
||||||
#if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")
|
#if ($column.htmlType == "radio" && ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short"))
|
||||||
#set ($hasGetIntDictOptions = 1)
|
#set ($hasGetIntDictOptions = 1)
|
||||||
#elseif ($column.javaType == "String")
|
#elseif ($column.htmlType == "radio" && $column.javaType == "String")
|
||||||
#set ($hasGetStrDictOptions = 1)
|
#set ($hasGetStrDictOptions = 1)
|
||||||
#elseif ($column.javaType == "Boolean")
|
#elseif ($column.htmlType == "radio" && $column.javaType == "Boolean")
|
||||||
#set ($hasGetBoolDictOptions = 1)
|
#set ($hasGetBoolDictOptions = 1)
|
||||||
#else
|
#elseif ($column.htmlType == "radio")
|
||||||
#set ($hasGetDictOptions = 1)
|
#set ($hasGetDictOptions = 1)
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
|
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey
|
||||||
|
&& (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate"))
|
||||||
|
#set ($hasDate = 1)
|
||||||
|
#end
|
||||||
|
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey
|
||||||
|
&& ${column.javaType.toLowerCase()} == "localdatetime")
|
||||||
|
#set ($hasDateTime = 1)
|
||||||
|
#end
|
||||||
#end
|
#end
|
||||||
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { useToast } from 'wot-design-uni'
|
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||||
import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
||||||
#if ($hasDict == 1)
|
#if ($hasGetDictOptions == 1 || $hasGetIntDictOptions == 1 || $hasGetStrDictOptions == 1 || $hasGetBoolDictOptions == 1)
|
||||||
#set ($dictImportNames = "")
|
#set ($dictImportNames = "")
|
||||||
#if ($hasGetDictOptions == 1)
|
#if ($hasGetDictOptions == 1)
|
||||||
#set ($dictImportNames = "${dictImportNames}getDictOptions, ")
|
#set ($dictImportNames = "${dictImportNames}getDictOptions, ")
|
||||||
|
|
@ -157,10 +204,23 @@ import { create${simpleClassName}, get${simpleClassName}, update${simpleClassNam
|
||||||
#set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1))
|
#set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1))
|
||||||
import { $dictImportNames } from '@/hooks/useDict'
|
import { $dictImportNames } from '@/hooks/useDict'
|
||||||
#end
|
#end
|
||||||
import { navigateBackPlus } from '@/utils'
|
import { delay, navigateBackPlus } from '@/utils'
|
||||||
#if ($hasDict == 1)
|
#if ($hasDict == 1)
|
||||||
import { DICT_TYPE } from '@/utils/constants'
|
import { DICT_TYPE } from '@/utils/constants'
|
||||||
#end
|
#end
|
||||||
|
#if ($hasDate == 1 || $hasDateTime == 1)
|
||||||
|
#set ($dateImportNames = "")
|
||||||
|
#if ($hasDate == 1)
|
||||||
|
#set ($dateImportNames = "${dateImportNames}formatDate, ")
|
||||||
|
#end
|
||||||
|
#if ($hasDateTime == 1)
|
||||||
|
#set ($dateImportNames = "${dateImportNames}formatDateTime, ")
|
||||||
|
#end
|
||||||
|
#set ($dateImportNames = $dateImportNames.trim())
|
||||||
|
#set ($dateImportNames = $dateImportNames.substring(0, $dateImportNames.length() - 1))
|
||||||
|
import { $dateImportNames } from '@/utils/date'
|
||||||
|
#end
|
||||||
|
import { createFormSchema } from '@/utils/wot'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id?: ${primaryTsType} | any
|
id?: ${primaryTsType} | any
|
||||||
|
|
@ -196,7 +256,12 @@ const formData = ref<${simpleClassName}>({
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
}) // 表单数据
|
}) // 表单数据
|
||||||
const formRules = {
|
#foreach($column in $columns)
|
||||||
|
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime"))
|
||||||
|
const ${column.javaField}Visible = ref(false) // ${column.columnComment}选择器状态
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
const formSchema = createFormSchema({
|
||||||
#foreach($column in $columns)
|
#foreach($column in $columns)
|
||||||
#set ($javaFieldLower = $column.javaField.toLowerCase())
|
#set ($javaFieldLower = $column.javaField.toLowerCase())
|
||||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey
|
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey
|
||||||
|
|
@ -204,7 +269,7 @@ const formRules = {
|
||||||
${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }],
|
${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }],
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
} // 表单校验规则
|
}) // 表单校验规则
|
||||||
const formRef = ref<FormInstance>() // 表单组件引用
|
const formRef = ref<FormInstance>() // 表单组件引用
|
||||||
|
|
||||||
/** 返回上一页 */
|
/** 返回上一页 */
|
||||||
|
|
@ -237,9 +302,7 @@ async function handleSubmit() {
|
||||||
toast.success('新增成功')
|
toast.success('新增成功')
|
||||||
}
|
}
|
||||||
uni.$emit('${table.moduleName}:${table.businessName}:reload')
|
uni.$emit('${table.moduleName}:${table.businessName}:reload')
|
||||||
setTimeout(() => {
|
delay(handleBack)
|
||||||
handleBack()
|
|
||||||
}, 500)
|
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,21 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
|
||||||
protected void writeResult(Map<String, String> result, String basePath) {
|
protected void writeResult(Map<String, String> result, String basePath) {
|
||||||
// 写入文件内容
|
// 写入文件内容
|
||||||
List<Map<String, String>> asserts = new ArrayList<>();
|
List<Map<String, String>> asserts = new ArrayList<>();
|
||||||
|
Set<String> usedContentPaths = new LinkedHashSet<>();
|
||||||
result.forEach((filePath, fileContent) -> {
|
result.forEach((filePath, fileContent) -> {
|
||||||
String lastFilePath = StrUtil.subAfter(filePath, '/', true);
|
String lastFilePath = StrUtil.subAfter(filePath, '/', true);
|
||||||
String contentPath = StrUtil.subAfter(lastFilePath, '.', true)
|
String ext = StrUtil.subAfter(lastFilePath, '.', true);
|
||||||
+ '/' + StrUtil.subBefore(lastFilePath, '.', true);
|
String name = StrUtil.subBefore(lastFilePath, '.', true);
|
||||||
|
String contentPath = ext + '/' + name;
|
||||||
|
// 同名文件(如 index.vue 同时出现在 列表/form/detail)会撞名,撞名时前缀补上级目录区分;仍撞则加序号兜底,避免快照互相覆盖
|
||||||
|
if (usedContentPaths.contains(contentPath)) {
|
||||||
|
String parentDir = StrUtil.subAfter(StrUtil.subBefore(filePath, '/' + lastFilePath, true), '/', true);
|
||||||
|
contentPath = ext + '/' + parentDir + '/' + name;
|
||||||
|
for (int i = 2; usedContentPaths.contains(contentPath); i++) {
|
||||||
|
contentPath = ext + '/' + parentDir + '/' + name + '-' + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usedContentPaths.add(contentPath);
|
||||||
asserts.add(MapUtil.<String, String>builder().put("filePath", filePath)
|
asserts.add(MapUtil.<String, String>builder().put("filePath", filePath)
|
||||||
.put("contentPath", contentPath).build());
|
.put("contentPath", contentPath).build());
|
||||||
FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath);
|
FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath);
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,10 @@
|
||||||
import type { Student } from '@/api/infra/demo'
|
import type { Student } from '@/api/infra/demo'
|
||||||
import { onUnload } from '@dcloudio/uni-app'
|
import { onUnload } from '@dcloudio/uni-app'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useToast } from 'wot-design-uni'
|
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||||
import { deleteStudent, getStudent } from '@/api/infra/demo'
|
import { deleteStudent, getStudent } from '@/api/infra/demo'
|
||||||
import { useAccess } from '@/hooks/useAccess'
|
import { useAccess } from '@/hooks/useAccess'
|
||||||
import { navigateBackPlus } from '@/utils'
|
import { delay, navigateBackPlus } from '@/utils'
|
||||||
import { DICT_TYPE } from '@/utils/constants'
|
import { DICT_TYPE } from '@/utils/constants'
|
||||||
import { formatDateTime } from '@/utils/date'
|
import { formatDateTime } from '@/utils/date'
|
||||||
|
|
||||||
|
|
@ -116,9 +116,7 @@ function handleDelete() {
|
||||||
await deleteStudent(props.id)
|
await deleteStudent(props.id)
|
||||||
toast.success('删除成功')
|
toast.success('删除成功')
|
||||||
uni.$emit('infra:demo:reload')
|
uni.$emit('infra:demo:reload')
|
||||||
setTimeout(() => {
|
delay(handleBack)
|
||||||
handleBack()
|
|
||||||
}, 500)
|
|
||||||
} finally {
|
} finally {
|
||||||
deleting.value = false
|
deleting.value = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,45 +9,48 @@
|
||||||
|
|
||||||
<!-- 表单区域 -->
|
<!-- 表单区域 -->
|
||||||
<view>
|
<view>
|
||||||
<wd-form ref="formRef" :model="formData" :rules="formRules">
|
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||||
<wd-cell-group border>
|
<wd-cell-group border>
|
||||||
<wd-input
|
<wd-form-item title="名字" title-width="180rpx" prop="name">
|
||||||
v-model="formData.name"
|
<wd-input
|
||||||
label="名字"
|
v-model="formData.name"
|
||||||
label-width="180rpx"
|
clearable
|
||||||
prop="name"
|
placeholder="请输入名字"
|
||||||
clearable
|
/>
|
||||||
placeholder="请输入名字"
|
</wd-form-item>
|
||||||
/>
|
<wd-form-item title="简介" title-width="180rpx" prop="description">
|
||||||
<wd-textarea
|
<wd-textarea
|
||||||
v-model="formData.description"
|
v-model="formData.description"
|
||||||
label="简介"
|
clearable
|
||||||
label-width="180rpx"
|
placeholder="请输入简介"
|
||||||
placeholder="请输入简介"
|
:maxlength="200"
|
||||||
:maxlength="200"
|
show-word-limit
|
||||||
show-word-limit
|
/>
|
||||||
clearable
|
</wd-form-item>
|
||||||
/>
|
<wd-form-item
|
||||||
<wd-datetime-picker
|
title="出生日期"
|
||||||
v-model="formData.birthday"
|
title-width="180rpx"
|
||||||
type="datetime"
|
|
||||||
label="出生日期"
|
|
||||||
label-width="180rpx"
|
|
||||||
prop="birthday"
|
prop="birthday"
|
||||||
|
is-link
|
||||||
|
:value="formatDateTime(formData.birthday)"
|
||||||
|
placeholder="请选择出生日期"
|
||||||
|
@click="birthdayVisible = true"
|
||||||
/>
|
/>
|
||||||
<wd-cell title="性别" title-width="180rpx" prop="sex" center>
|
<wd-datetime-picker
|
||||||
<wd-radio-group v-model="formData.sex" shape="button">
|
v-model="formData.birthday"
|
||||||
<wd-radio
|
v-model:visible="birthdayVisible"
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
type="datetime"
|
||||||
:key="dict.value"
|
/>
|
||||||
:value="dict.value"
|
<yd-form-picker
|
||||||
>
|
v-model="formData.sex"
|
||||||
{{ dict.label }}
|
label="性别"
|
||||||
</wd-radio>
|
label-width="180rpx"
|
||||||
</wd-radio-group>
|
prop="sex"
|
||||||
</wd-cell>
|
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||||
<wd-cell title="是否有效" title-width="180rpx" prop="enabled" center>
|
placeholder="请选择性别"
|
||||||
<wd-radio-group v-model="formData.enabled" shape="button">
|
/>
|
||||||
|
<wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center>
|
||||||
|
<wd-radio-group v-model="formData.enabled" type="button">
|
||||||
<wd-radio
|
<wd-radio
|
||||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
|
|
@ -56,31 +59,20 @@
|
||||||
{{ dict.label }}
|
{{ dict.label }}
|
||||||
</wd-radio>
|
</wd-radio>
|
||||||
</wd-radio-group>
|
</wd-radio-group>
|
||||||
</wd-cell>
|
</wd-form-item>
|
||||||
<wd-input
|
<wd-form-item title="头像" title-width="180rpx" prop="avatar">
|
||||||
v-model="formData.avatar"
|
<yd-upload-img v-model="formData.avatar" directory="infra/demo" />
|
||||||
label="头像"
|
</wd-form-item>
|
||||||
label-width="180rpx"
|
<wd-form-item title="附件" title-width="180rpx" prop="video">
|
||||||
prop="avatar"
|
<yd-upload-file v-model="formData.video" directory="infra/demo" />
|
||||||
clearable
|
</wd-form-item>
|
||||||
placeholder="请输入头像"
|
<wd-form-item title="备注" title-width="180rpx" prop="memo">
|
||||||
/>
|
<wd-input
|
||||||
<wd-input
|
v-model="formData.memo"
|
||||||
v-model="formData.video"
|
clearable
|
||||||
label="附件"
|
placeholder="请输入备注"
|
||||||
label-width="180rpx"
|
/>
|
||||||
prop="video"
|
</wd-form-item>
|
||||||
clearable
|
|
||||||
placeholder="请输入附件"
|
|
||||||
/>
|
|
||||||
<wd-input
|
|
||||||
v-model="formData.memo"
|
|
||||||
label="备注"
|
|
||||||
label-width="180rpx"
|
|
||||||
prop="memo"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入备注"
|
|
||||||
/>
|
|
||||||
</wd-cell-group>
|
</wd-cell-group>
|
||||||
</wd-form>
|
</wd-form>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -100,14 +92,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
|
import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
|
||||||
import type { Student } from '@/api/infra/demo'
|
import type { Student } from '@/api/infra/demo'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { useToast } from 'wot-design-uni'
|
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||||
import { createStudent, getStudent, updateStudent } from '@/api/infra/demo'
|
import { createStudent, getStudent, updateStudent } from '@/api/infra/demo'
|
||||||
import { getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict'
|
import { getBoolDictOptions } from '@/hooks/useDict'
|
||||||
import { navigateBackPlus } from '@/utils'
|
import { delay, navigateBackPlus } from '@/utils'
|
||||||
import { DICT_TYPE } from '@/utils/constants'
|
import { DICT_TYPE } from '@/utils/constants'
|
||||||
|
import { formatDateTime } from '@/utils/date'
|
||||||
|
import { createFormSchema } from '@/utils/wot'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id?: number | any
|
id?: number | any
|
||||||
|
|
@ -134,7 +128,8 @@ const formData = ref<Student>({
|
||||||
video: '',
|
video: '',
|
||||||
memo: '',
|
memo: '',
|
||||||
}) // 表单数据
|
}) // 表单数据
|
||||||
const formRules = {
|
const birthdayVisible = ref(false) // 出生日期选择器状态
|
||||||
|
const formSchema = createFormSchema({
|
||||||
name: [{ required: true, message: '名字不能为空' }],
|
name: [{ required: true, message: '名字不能为空' }],
|
||||||
description: [{ required: true, message: '简介不能为空' }],
|
description: [{ required: true, message: '简介不能为空' }],
|
||||||
birthday: [{ required: true, message: '出生日期不能为空' }],
|
birthday: [{ required: true, message: '出生日期不能为空' }],
|
||||||
|
|
@ -143,7 +138,7 @@ const formRules = {
|
||||||
avatar: [{ required: true, message: '头像不能为空' }],
|
avatar: [{ required: true, message: '头像不能为空' }],
|
||||||
video: [{ required: true, message: '附件不能为空' }],
|
video: [{ required: true, message: '附件不能为空' }],
|
||||||
memo: [{ required: true, message: '备注不能为空' }],
|
memo: [{ required: true, message: '备注不能为空' }],
|
||||||
} // 表单校验规则
|
}) // 表单校验规则
|
||||||
const formRef = ref<FormInstance>() // 表单组件引用
|
const formRef = ref<FormInstance>() // 表单组件引用
|
||||||
|
|
||||||
/** 返回上一页 */
|
/** 返回上一页 */
|
||||||
|
|
@ -176,9 +171,7 @@ async function handleSubmit() {
|
||||||
toast.success('新增成功')
|
toast.success('新增成功')
|
||||||
}
|
}
|
||||||
uni.$emit('infra:demo:reload')
|
uni.$emit('infra:demo:reload')
|
||||||
setTimeout(() => {
|
delay(handleBack)
|
||||||
handleBack()
|
|
||||||
}, 500)
|
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
<view class="yd-search-form-label">
|
<view class="yd-search-form-label">
|
||||||
性别
|
性别
|
||||||
</view>
|
</view>
|
||||||
<wd-radio-group v-model="formData.sex" shape="button">
|
<wd-radio-group v-model="formData.sex" type="button">
|
||||||
<wd-radio :value="-1">
|
<wd-radio :value="-1">
|
||||||
全部
|
全部
|
||||||
</wd-radio>
|
</wd-radio>
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
<view class="yd-search-form-label">
|
<view class="yd-search-form-label">
|
||||||
是否有效
|
是否有效
|
||||||
</view>
|
</view>
|
||||||
<wd-radio-group v-model="formData.enabled" shape="button">
|
<wd-radio-group v-model="formData.enabled" type="button">
|
||||||
<wd-radio :value="-1">
|
<wd-radio :value="-1">
|
||||||
全部
|
全部
|
||||||
</wd-radio>
|
</wd-radio>
|
||||||
|
|
@ -61,42 +61,7 @@
|
||||||
</wd-radio>
|
</wd-radio>
|
||||||
</wd-radio-group>
|
</wd-radio-group>
|
||||||
</view>
|
</view>
|
||||||
<view class="yd-search-form-item">
|
<yd-search-date-range v-model="formData.createTime" label="创建时间" />
|
||||||
<view class="yd-search-form-label">
|
|
||||||
创建时间
|
|
||||||
</view>
|
|
||||||
<view class="yd-search-form-date-range-container">
|
|
||||||
<view @click="visibleCreateTime[0] = true">
|
|
||||||
<view class="yd-search-form-date-range-picker">
|
|
||||||
{{ formatDate(formData.createTime?.[0]) || '开始日期' }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
-
|
|
||||||
<view @click="visibleCreateTime[1] = true">
|
|
||||||
<view class="yd-search-form-date-range-picker">
|
|
||||||
{{ formatDate(formData.createTime?.[1]) || '结束日期' }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<wd-datetime-picker-view v-if="visibleCreateTime[0]" v-model="tempCreateTime[0]" type="date" />
|
|
||||||
<view v-if="visibleCreateTime[0]" class="yd-search-form-date-range-actions">
|
|
||||||
<wd-button size="small" plain @click="visibleCreateTime[0] = false">
|
|
||||||
取消
|
|
||||||
</wd-button>
|
|
||||||
<wd-button size="small" type="primary" @click="handleCreateTime0Confirm">
|
|
||||||
确定
|
|
||||||
</wd-button>
|
|
||||||
</view>
|
|
||||||
<wd-datetime-picker-view v-if="visibleCreateTime[1]" v-model="tempCreateTime[1]" type="date" />
|
|
||||||
<view v-if="visibleCreateTime[1]" class="yd-search-form-date-range-actions">
|
|
||||||
<wd-button size="small" plain @click="visibleCreateTime[1] = false">
|
|
||||||
取消
|
|
||||||
</wd-button>
|
|
||||||
<wd-button size="small" type="primary" @click="handleCreateTime1Confirm">
|
|
||||||
确定
|
|
||||||
</wd-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="yd-search-form-actions">
|
<view class="yd-search-form-actions">
|
||||||
<wd-button class="flex-1" plain @click="handleReset">
|
<wd-button class="flex-1" plain @click="handleReset">
|
||||||
重置
|
重置
|
||||||
|
|
@ -151,21 +116,6 @@ const placeholder = computed(() => {
|
||||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索学生'
|
return conditions.length > 0 ? conditions.join(' | ') : '搜索学生'
|
||||||
})
|
})
|
||||||
|
|
||||||
const visibleCreateTime = ref<[boolean, boolean]>([false, false]) // 创建时间选择器状态
|
|
||||||
const tempCreateTime = ref<[number, number]>([Date.now(), Date.now()]) // 创建时间临时值
|
|
||||||
|
|
||||||
/** 确认创建时间开始日期 */
|
|
||||||
function handleCreateTime0Confirm() {
|
|
||||||
formData.createTime = [tempCreateTime.value[0], formData.createTime?.[1]]
|
|
||||||
visibleCreateTime.value[0] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 确认创建时间结束日期 */
|
|
||||||
function handleCreateTime1Confirm() {
|
|
||||||
formData.createTime = [formData.createTime?.[0], tempCreateTime.value[1]]
|
|
||||||
visibleCreateTime.value[1] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
package cn.iocoder.yudao.module.iot.framework.tdengine.core;
|
package cn.iocoder.yudao.module.iot.framework.tdengine.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TDEngine 表字段
|
* TDEngine 表字段
|
||||||
*/
|
*/
|
||||||
|
|
@ -61,4 +64,15 @@ public class TDengineTableField {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建字段名:TDengine 默认会将字段名转为小写,需要和建表、写入、查询保持一致。
|
||||||
|
* 例如:PT -> pt,PfT -> pf_t。
|
||||||
|
*/
|
||||||
|
public static String buildFieldName(String field) {
|
||||||
|
if (StrUtil.isBlank(field)) {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
return StrUtil.toUnderlineCase(field).toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
|
||||||
private List<TDengineTableField> buildTableFieldList(List<IotThingModelDO> thingModels) {
|
private List<TDengineTableField> buildTableFieldList(List<IotThingModelDO> thingModels) {
|
||||||
return convertList(thingModels, thingModel -> {
|
return convertList(thingModels, thingModel -> {
|
||||||
TDengineTableField field = new TDengineTableField(
|
TDengineTableField field = new TDengineTableField(
|
||||||
StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写
|
TDengineTableField.buildFieldName(thingModel.getIdentifier()),
|
||||||
TYPE_MAPPING.get(thingModel.getProperty().getDataType()));
|
TYPE_MAPPING.get(thingModel.getProperty().getDataType()));
|
||||||
String dataType = thingModel.getProperty().getDataType();
|
String dataType = thingModel.getProperty().getDataType();
|
||||||
if (Objects.equals(dataType, IotDataSpecsDataTypeEnum.TEXT.getDataType())) {
|
if (Objects.equals(dataType, IotDataSpecsDataTypeEnum.TEXT.getDataType())) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.text.CharPool;
|
import cn.hutool.core.text.CharPool;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotCurrentTimeConditionMatcher;
|
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotCurrentTimeConditionMatcher;
|
||||||
import cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotTimerConditionEvaluator;
|
import cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotTimerConditionEvaluator;
|
||||||
|
|
@ -11,7 +12,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ public class IotSceneRuleTimeHelper {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
if (isDateTimeOperator(operatorEnum)) {
|
if (isDateTimeOperator(operatorEnum)) {
|
||||||
// 日期时间匹配(时间戳,秒级)
|
// 日期时间匹配(时间戳,秒级)
|
||||||
long currentTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond();
|
long currentTimestamp = LocalDateTimeUtils.toEpochSecond(now);
|
||||||
return matchDateTime(currentTimestamp, operatorEnum, param);
|
return matchDateTime(currentTimestamp, operatorEnum, param);
|
||||||
} else {
|
} else {
|
||||||
// 当日时间匹配(HH:mm:ss)
|
// 当日时间匹配(HH:mm:ss)
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
TAGS ('${device.id}')
|
TAGS ('${device.id}')
|
||||||
(ts, report_time,
|
(ts, report_time,
|
||||||
<foreach item="key" collection="properties.keys" separator=",">
|
<foreach item="key" collection="properties.keys" separator=",">
|
||||||
${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)}
|
${@cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField@buildFieldName(key)}
|
||||||
</foreach>
|
</foreach>
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
|
|
@ -68,12 +68,12 @@
|
||||||
|
|
||||||
<select id="selectListByHistory"
|
<select id="selectListByHistory"
|
||||||
resultType="cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO">
|
resultType="cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO">
|
||||||
SELECT ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} AS `value`, ts AS update_time
|
SELECT ${@cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField@buildFieldName(reqVO.identifier)} AS `value`, ts AS update_time
|
||||||
FROM device_property_${reqVO.deviceId}
|
FROM device_property_${reqVO.deviceId}
|
||||||
WHERE ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} IS NOT NULL
|
WHERE ${@cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField@buildFieldName(reqVO.identifier)} IS NOT NULL
|
||||||
AND ts BETWEEN ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[0])}
|
AND ts BETWEEN ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[0])}
|
||||||
AND ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])}
|
AND ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])}
|
||||||
ORDER BY ts DESC
|
ORDER BY ts DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package cn.iocoder.yudao.module.iot.framework.tdengine.core;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
public class TDengineTableFieldTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildFieldName() {
|
||||||
|
assertEquals("ua", TDengineTableField.buildFieldName("Ua"));
|
||||||
|
assertEquals("pf_t", TDengineTableField.buildFieldName("PfT"));
|
||||||
|
assertEquals("pt", TDengineTableField.buildFieldName("PT"));
|
||||||
|
assertEquals("pa", TDengineTableField.buildFieldName("PA"));
|
||||||
|
assertEquals("geo_location", TDengineTableField.buildFieldName("GeoLocation"));
|
||||||
|
assertEquals("light_status", TDengineTableField.buildFieldName("LightStatus"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildFieldName_blank() {
|
||||||
|
assertNull(TDengineTableField.buildFieldName(null));
|
||||||
|
assertEquals("", TDengineTableField.buildFieldName(""));
|
||||||
|
assertEquals(" ", TDengineTableField.buildFieldName(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,11 +5,15 @@ import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty;
|
||||||
import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;
|
import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper;
|
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper;
|
||||||
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
|
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
|
||||||
|
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||||
import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
|
import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
@ -17,8 +21,12 @@ import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
@ -38,6 +46,8 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
||||||
@Mock
|
@Mock
|
||||||
private IotThingModelService thingModelService;
|
private IotThingModelService thingModelService;
|
||||||
@Mock
|
@Mock
|
||||||
|
private IotProductService productService;
|
||||||
|
@Mock
|
||||||
private IotDevicePropertyMapper devicePropertyMapper;
|
private IotDevicePropertyMapper devicePropertyMapper;
|
||||||
@Mock
|
@Mock
|
||||||
private DevicePropertyRedisDAO deviceDataRedisDAO;
|
private DevicePropertyRedisDAO deviceDataRedisDAO;
|
||||||
|
|
@ -100,12 +110,15 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
||||||
params.put("Temperature", "abc");
|
params.put("Temperature", "abc");
|
||||||
IotDeviceMessage message = buildMessage(params);
|
IotDeviceMessage message = buildMessage(params);
|
||||||
|
|
||||||
|
// mock 方法
|
||||||
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
||||||
.thenReturn(singletonList(temperature));
|
.thenReturn(singletonList(temperature));
|
||||||
when(thingModelService.convertThingModelPropertyValue(temperature, "abc")).thenReturn(null);
|
when(thingModelService.convertThingModelPropertyValue(temperature, "abc")).thenReturn(null);
|
||||||
|
|
||||||
|
// 调用,并断言:不会抛出异常
|
||||||
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
||||||
|
|
||||||
|
// 断言:没有合法属性,不会写入 TDengine 与 Redis
|
||||||
verify(devicePropertyMapper, never()).insert(any(), any(), anyLong(), anyLong());
|
verify(devicePropertyMapper, never()).insert(any(), any(), anyLong(), anyLong());
|
||||||
verify(deviceDataRedisDAO, never()).putAll(anyLong(), any());
|
verify(deviceDataRedisDAO, never()).putAll(anyLong(), any());
|
||||||
}
|
}
|
||||||
|
|
@ -119,11 +132,14 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
||||||
params.put("Temperature", null);
|
params.put("Temperature", null);
|
||||||
IotDeviceMessage message = buildMessage(params);
|
IotDeviceMessage message = buildMessage(params);
|
||||||
|
|
||||||
|
// mock 方法
|
||||||
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
||||||
.thenReturn(singletonList(thingModel));
|
.thenReturn(singletonList(thingModel));
|
||||||
|
|
||||||
|
// 调用,并断言:不会抛出异常
|
||||||
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
||||||
|
|
||||||
|
// 断言:跳过空值,不会转换属性值,也不会写入 TDengine 与 Redis
|
||||||
verify(thingModelService, never()).convertThingModelPropertyValue(any(), any());
|
verify(thingModelService, never()).convertThingModelPropertyValue(any(), any());
|
||||||
verify(devicePropertyMapper, never()).insert(any(), any(), anyLong(), anyLong());
|
verify(devicePropertyMapper, never()).insert(any(), any(), anyLong(), anyLong());
|
||||||
verify(deviceDataRedisDAO, never()).putAll(anyLong(), any());
|
verify(deviceDataRedisDAO, never()).putAll(anyLong(), any());
|
||||||
|
|
@ -139,17 +155,48 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
||||||
params.put("PowerSwitch", true);
|
params.put("PowerSwitch", true);
|
||||||
IotDeviceMessage message = buildMessage(params);
|
IotDeviceMessage message = buildMessage(params);
|
||||||
|
|
||||||
|
// mock 方法
|
||||||
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
||||||
.thenReturn(singletonList(thingModel));
|
.thenReturn(singletonList(thingModel));
|
||||||
when(thingModelService.convertThingModelPropertyValue(thingModel, true)).thenReturn((byte) 1);
|
when(thingModelService.convertThingModelPropertyValue(thingModel, true)).thenReturn((byte) 1);
|
||||||
|
|
||||||
|
// 调用,并断言:非字符串 key 不影响其它合法属性
|
||||||
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
||||||
|
|
||||||
|
// 断言:只写入合法属性
|
||||||
Map<String, Object> dbProperties = captureMapperInsertProperties();
|
Map<String, Object> dbProperties = captureMapperInsertProperties();
|
||||||
assertEquals(1, dbProperties.size());
|
assertEquals(1, dbProperties.size());
|
||||||
assertEquals((byte) 1, dbProperties.get("PowerSwitch"));
|
assertEquals((byte) 1, dbProperties.get("PowerSwitch"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefineDevicePropertyData_fieldNameToLowerCase() {
|
||||||
|
// 准备参数:全大写缩写和驼峰缩写都需要转换为 TDengine 实际的小写字段名
|
||||||
|
Long productId = 2L;
|
||||||
|
IotProductDO product = IotProductDO.builder().id(productId).build();
|
||||||
|
List<IotThingModelDO> thingModels = Arrays.asList(
|
||||||
|
buildThingModel("Ua", IotDataSpecsDataTypeEnum.FLOAT.getDataType()),
|
||||||
|
buildThingModel("PfT", IotDataSpecsDataTypeEnum.FLOAT.getDataType()),
|
||||||
|
buildThingModel("PT", IotDataSpecsDataTypeEnum.FLOAT.getDataType()),
|
||||||
|
buildThingModel("PA", IotDataSpecsDataTypeEnum.FLOAT.getDataType()));
|
||||||
|
thingModels.forEach(thingModel -> thingModel.setType(IotThingModelTypeEnum.PROPERTY.getType()));
|
||||||
|
|
||||||
|
// mock 方法
|
||||||
|
when(productService.validateProductExists(productId)).thenReturn(product);
|
||||||
|
when(thingModelService.getThingModelListByProductId(productId)).thenReturn(thingModels);
|
||||||
|
when(devicePropertyMapper.getProductPropertySTableFieldList(productId)).thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
service.defineDevicePropertyData(productId);
|
||||||
|
|
||||||
|
// 断言:字段名统一为小写下划线,避免 PT 和数据库中的 pt 被误判为不同字段
|
||||||
|
ArgumentCaptor<List<TDengineTableField>> captor = ArgumentCaptor.forClass(List.class);
|
||||||
|
verify(devicePropertyMapper).createProductPropertySTable(eq(productId), captor.capture());
|
||||||
|
assertEquals(Arrays.asList("ua", "pf_t", "pt", "pa"), captor.getValue().stream()
|
||||||
|
.map(TDengineTableField::getField)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 辅助方法 ==========
|
// ========== 辅助方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.*;
|
||||||
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
|
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
|
||||||
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
|
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
@ -35,6 +36,15 @@ public class ProductCommentController {
|
||||||
return success(BeanUtils.toBean(pageResult, ProductCommentRespVO.class));
|
return success(BeanUtils.toBean(pageResult, ProductCommentRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得商品评价")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('product:comment:query')")
|
||||||
|
public CommonResult<ProductCommentRespVO> getComment(@RequestParam("id") Long id) {
|
||||||
|
ProductCommentDO comment = productCommentService.getComment(id);
|
||||||
|
return success(BeanUtils.toBean(comment, ProductCommentRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping("/update-visible")
|
@PutMapping("/update-visible")
|
||||||
@Operation(summary = "显示 / 隐藏评论")
|
@Operation(summary = "显示 / 隐藏评论")
|
||||||
@PreAuthorize("@ss.hasPermission('product:comment:update')")
|
@PreAuthorize("@ss.hasPermission('product:comment:update')")
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,10 @@ public class ProductSpuController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/get-count")
|
@GetMapping("/get-count")
|
||||||
@Operation(summary = "获得商品 SPU 分页 tab count")
|
@Operation(summary = "获得商品 SPU 分页 tab count(支持按 name/categoryId/createTime 筛选)")
|
||||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||||
public CommonResult<Map<Integer, Long>> getSpuCount() {
|
public CommonResult<Map<Integer, Long>> getSpuCount(ProductSpuPageReqVO reqVO) {
|
||||||
return success(productSpuService.getTabsCount());
|
return success(productSpuService.getTabsCount(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export-excel")
|
@GetMapping("/export-excel")
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,22 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按筛选条件 + Tab 统计商品 SPU 数量(用于带筛选的 tab count)
|
||||||
|
*
|
||||||
|
* @param reqVO 筛选条件(name/categoryId/createTime)
|
||||||
|
* @param tabType Tab 标签类型
|
||||||
|
* @return 数量
|
||||||
|
*/
|
||||||
|
default Long selectCountByTab(ProductSpuPageReqVO reqVO, Integer tabType) {
|
||||||
|
LambdaQueryWrapperX<ProductSpuDO> queryWrapper = new LambdaQueryWrapperX<ProductSpuDO>()
|
||||||
|
.likeIfPresent(ProductSpuDO::getName, reqVO.getName())
|
||||||
|
.eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())
|
||||||
|
.betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime());
|
||||||
|
appendTabQuery(tabType, queryWrapper);
|
||||||
|
return selectCount(queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新商品 SPU 浏览量
|
* 更新商品 SPU 浏览量
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,14 @@ public interface ProductCommentService {
|
||||||
*/
|
*/
|
||||||
void replyComment(ProductCommentReplyReqVO replyVO, Long userId);
|
void replyComment(ProductCommentReplyReqVO replyVO, Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【管理员】获得商品评价
|
||||||
|
*
|
||||||
|
* @param id 评价编号
|
||||||
|
* @return 商品评价
|
||||||
|
*/
|
||||||
|
ProductCommentDO getComment(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 【管理员】获得商品评价分页
|
* 【管理员】获得商品评价分页
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,11 @@ public class ProductCommentServiceImpl implements ProductCommentService {
|
||||||
return productCommentMapper.selectPage(pageVO, visible);
|
return productCommentMapper.selectPage(pageVO, visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProductCommentDO getComment(Long id) {
|
||||||
|
return validateCommentExists(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) {
|
public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) {
|
||||||
return productCommentMapper.selectPage(pageReqVO);
|
return productCommentMapper.selectPage(pageReqVO);
|
||||||
|
|
|
||||||
|
|
@ -118,11 +118,12 @@ public interface ProductSpuService {
|
||||||
void updateSpuStatus(ProductSpuUpdateStatusReqVO updateReqVO);
|
void updateSpuStatus(ProductSpuUpdateStatusReqVO updateReqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 SPU 列表标签对应的 Count 数量
|
* 获取 SPU 列表标签对应的 Count 数量(支持按 name/categoryId/createTime 筛选)
|
||||||
*
|
*
|
||||||
|
* @param reqVO 筛选条件
|
||||||
* @return Count 数量
|
* @return Count 数量
|
||||||
*/
|
*/
|
||||||
Map<Integer, Long> getTabsCount();
|
Map<Integer, Long> getTabsCount(ProductSpuPageReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过分类 categoryId 查询 SPU 个数
|
* 通过分类 categoryId 查询 SPU 个数
|
||||||
|
|
|
||||||
|
|
@ -256,23 +256,19 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Integer, Long> getTabsCount() {
|
public Map<Integer, Long> getTabsCount(ProductSpuPageReqVO reqVO) {
|
||||||
Map<Integer, Long> counts = Maps.newLinkedHashMapWithExpectedSize(5);
|
Map<Integer, Long> counts = Maps.newLinkedHashMapWithExpectedSize(5);
|
||||||
// 查询销售中的商品数量
|
// 每个 tab 的数量 = 筛选条件(name/categoryId/createTime)+ 该 tab 的状态/库存条件
|
||||||
counts.put(ProductSpuPageReqVO.FOR_SALE,
|
counts.put(ProductSpuPageReqVO.FOR_SALE,
|
||||||
productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()));
|
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.FOR_SALE));
|
||||||
// 查询仓库中的商品数量
|
|
||||||
counts.put(ProductSpuPageReqVO.IN_WAREHOUSE,
|
counts.put(ProductSpuPageReqVO.IN_WAREHOUSE,
|
||||||
productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.DISABLE.getStatus()));
|
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.IN_WAREHOUSE));
|
||||||
// 查询售空的商品数量
|
|
||||||
counts.put(ProductSpuPageReqVO.SOLD_OUT,
|
counts.put(ProductSpuPageReqVO.SOLD_OUT,
|
||||||
productSpuMapper.selectCount(ProductSpuDO::getStock, 0));
|
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.SOLD_OUT));
|
||||||
// 查询触发警戒库存的商品数量
|
|
||||||
counts.put(ProductSpuPageReqVO.ALERT_STOCK,
|
counts.put(ProductSpuPageReqVO.ALERT_STOCK,
|
||||||
productSpuMapper.selectCount());
|
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.ALERT_STOCK));
|
||||||
// 查询回收站中的商品数量
|
|
||||||
counts.put(ProductSpuPageReqVO.RECYCLE_BIN,
|
counts.put(ProductSpuPageReqVO.RECYCLE_BIN,
|
||||||
productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));
|
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.RECYCLE_BIN));
|
||||||
return counts;
|
return counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
|
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
|
||||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponRespVO;
|
||||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponSendReqVO;
|
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponSendReqVO;
|
||||||
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
|
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
|
||||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||||
|
|
@ -19,8 +21,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import javax.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
@ -63,6 +65,15 @@ public class CouponController {
|
||||||
return success(pageResulVO);
|
return success(pageResulVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得优惠劵")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('promotion:coupon:query')")
|
||||||
|
public CommonResult<CouponRespVO> getCoupon(@RequestParam("id") Long id) {
|
||||||
|
CouponDO coupon = couponService.getCoupon(id);
|
||||||
|
return success(BeanUtils.toBean(coupon, CouponRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/send")
|
@PostMapping("/send")
|
||||||
@Operation(summary = "发送优惠劵")
|
@Operation(summary = "发送优惠劵")
|
||||||
@PreAuthorize("@ss.hasPermission('promotion:coupon:send')")
|
@PreAuthorize("@ss.hasPermission('promotion:coupon:send')")
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ public interface CouponService {
|
||||||
Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates);
|
Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得优惠劵
|
* 【会员】获得优惠劵
|
||||||
*
|
*
|
||||||
* @param userId 用户编号
|
* @param userId 用户编号
|
||||||
* @param id 编号
|
* @param id 编号
|
||||||
|
|
@ -168,4 +168,12 @@ public interface CouponService {
|
||||||
*/
|
*/
|
||||||
CouponDO getCoupon(Long userId, Long id);
|
CouponDO getCoupon(Long userId, Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【管理员】获得优惠劵
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return 优惠劵
|
||||||
|
*/
|
||||||
|
CouponDO getCoupon(Long id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,11 @@ public class CouponServiceImpl implements CouponService {
|
||||||
return couponMapper.selectByIdAndUserId(id, userId);
|
return couponMapper.selectByIdAndUserId(id, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CouponDO getCoupon(Long id) {
|
||||||
|
return couponMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
private CouponDO validateCouponExists(Long id) {
|
private CouponDO validateCouponExists(Long id) {
|
||||||
CouponDO coupon = couponMapper.selectById(id);
|
CouponDO coupon = couponMapper.selectById(id);
|
||||||
if (coupon == null) {
|
if (coupon == null) {
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ public class DeliveryPickUpStoreController {
|
||||||
}
|
}
|
||||||
List<AdminUserRespDTO> verifyUsers = CollUtil.isNotEmpty(deliveryPickUpStore.getVerifyUserIds()) ?
|
List<AdminUserRespDTO> verifyUsers = CollUtil.isNotEmpty(deliveryPickUpStore.getVerifyUserIds()) ?
|
||||||
adminUserApi.getUserList(deliveryPickUpStore.getVerifyUserIds()).getCheckedData() : null;
|
adminUserApi.getUserList(deliveryPickUpStore.getVerifyUserIds()).getCheckedData() : null;
|
||||||
return success(BeanUtils.toBean(deliveryPickUpStore, DeliveryPickUpStoreRespVO.class)
|
return success(DeliveryPickUpStoreConvert.INSTANCE.convert01(deliveryPickUpStore)
|
||||||
.setVerifyUsers(BeanUtils.toBean(verifyUsers, UserSimpleBaseVO.class)));
|
.setVerifyUsers(BeanUtils.toBean(verifyUsers, UserSimpleBaseVO.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ public class DeliveryPickUpStoreRespVO extends DeliveryPickUpStoreBaseVO {
|
||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "地区名称", example = "上海上海市普陀区")
|
||||||
|
private String areaName;
|
||||||
|
|
||||||
@Schema(description = "核销用户数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "核销用户数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private List<UserSimpleBaseVO> verifyUsers;
|
private List<UserSimpleBaseVO> verifyUsers;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ public interface DeliveryPickUpStoreConvert {
|
||||||
|
|
||||||
PageResult<DeliveryPickUpStoreRespVO> convertPage(PageResult<DeliveryPickUpStoreDO> page);
|
PageResult<DeliveryPickUpStoreRespVO> convertPage(PageResult<DeliveryPickUpStoreDO> page);
|
||||||
|
|
||||||
|
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||||
|
DeliveryPickUpStoreRespVO convert01(DeliveryPickUpStoreDO bean);
|
||||||
|
|
||||||
List<DeliveryPickUpStoreSimpleRespVO> convertList1(List<DeliveryPickUpStoreDO> list);
|
List<DeliveryPickUpStoreSimpleRespVO> convertList1(List<DeliveryPickUpStoreDO> list);
|
||||||
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||||
DeliveryPickUpStoreSimpleRespVO convert02(DeliveryPickUpStoreDO bean);
|
DeliveryPickUpStoreSimpleRespVO convert02(DeliveryPickUpStoreDO bean);
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,7 @@ public class AddressRespVO extends AddressBaseVO {
|
||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "地区名称", example = "上海上海市普陀区")
|
||||||
|
private String areaName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ public interface AddressConvert {
|
||||||
return AreaUtils.format(areaId);
|
return AreaUtils.format(areaId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||||
|
AddressRespVO convert03(MemberAddressDO bean);
|
||||||
|
|
||||||
List<AddressRespVO> convertList2(List<MemberAddressDO> list);
|
List<AddressRespVO> convertList2(List<MemberAddressDO> list);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public class SubscribeHandler implements WxMpMessageHandler {
|
||||||
} catch (WxErrorException e) {
|
} catch (WxErrorException e) {
|
||||||
log.error("[handle][粉丝({})] 获取粉丝信息失败!", wxMessage.getFromUser(), e);
|
log.error("[handle][粉丝({})] 获取粉丝信息失败!", wxMessage.getFromUser(), e);
|
||||||
// 特殊情况(个人账号,无接口权限):https://t.zsxq.com/cLFq5
|
// 特殊情况(个人账号,无接口权限):https://t.zsxq.com/cLFq5
|
||||||
if (ObjUtil.equal(e.getError().getErrorCode(), WxMpErrorMsgEnum.CODE_48001)) {
|
if (ObjUtil.equal(e.getError().getErrorCode(), WxMpErrorMsgEnum.CODE_48001.getCode())) {
|
||||||
wxMpUser = new WxMpUser();
|
wxMpUser = new WxMpUser();
|
||||||
wxMpUser.setOpenId(wxMessage.getFromUser());
|
wxMpUser.setOpenId(wxMessage.getFromUser());
|
||||||
wxMpUser.setSubscribe(true);
|
wxMpUser.setSubscribe(true);
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,15 @@ public class PayChannelController {
|
||||||
return success(PayChannelConvert.INSTANCE.convert(channel));
|
return success(PayChannelConvert.INSTANCE.convert(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/list")
|
||||||
|
@Operation(summary = "获得指定应用的支付渠道列表")
|
||||||
|
@Parameter(name = "appId", description = "应用编号", required = true, example = "1")
|
||||||
|
@PreAuthorize("@ss.hasPermission('pay:channel:query')")
|
||||||
|
public CommonResult<List<PayChannelRespVO>> getChannelList(@RequestParam("appId") Long appId) {
|
||||||
|
List<PayChannelDO> list = channelService.getChannelListByAppId(appId);
|
||||||
|
return success(PayChannelConvert.INSTANCE.convertList(list));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/get-enable-code-list")
|
@GetMapping("/get-enable-code-list")
|
||||||
@Operation(summary = "获得指定应用的开启的支付渠道编码列表")
|
@Operation(summary = "获得指定应用的开启的支付渠道编码列表")
|
||||||
@Parameter(name = "appId", description = "应用编号", required = true, example = "1")
|
@Parameter(name = "appId", description = "应用编号", required = true, example = "1")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.pay.convert.channel;
|
package cn.iocoder.yudao.module.pay.convert.channel;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelRespVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelRespVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
|
||||||
|
|
@ -9,6 +10,8 @@ import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface PayChannelConvert {
|
public interface PayChannelConvert {
|
||||||
|
|
||||||
|
|
@ -23,6 +26,15 @@ public interface PayChannelConvert {
|
||||||
@Mapping(target = "config",expression = "java(cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString(bean.getConfig()))")
|
@Mapping(target = "config",expression = "java(cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString(bean.getConfig()))")
|
||||||
PayChannelRespVO convert(PayChannelDO bean);
|
PayChannelRespVO convert(PayChannelDO bean);
|
||||||
|
|
||||||
|
default List<PayChannelRespVO> convertList(List<PayChannelDO> list) {
|
||||||
|
// 列表不下发 config(密钥/证书),详情接口再返回完整配置
|
||||||
|
return CollectionUtils.convertList(list, channel -> {
|
||||||
|
PayChannelRespVO vo = convert(channel);
|
||||||
|
vo.setConfig(null);
|
||||||
|
return vo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
PageResult<PayChannelRespVO> convertPage(PageResult<PayChannelDO> page);
|
PageResult<PayChannelRespVO> convertPage(PageResult<PayChannelDO> page);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,8 @@ public interface PayChannelMapper extends BaseMapperX<PayChannelDO> {
|
||||||
.eq(PayChannelDO::getStatus, status));
|
.eq(PayChannelDO::getStatus, status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default List<PayChannelDO> selectListByAppId(Long appId) {
|
||||||
|
return selectList(PayChannelDO::getAppId, appId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,14 @@ public interface PayChannelService {
|
||||||
*/
|
*/
|
||||||
List<PayChannelDO> getEnableChannelList(Long appId);
|
List<PayChannelDO> getEnableChannelList(Long appId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定应用的渠道列表
|
||||||
|
*
|
||||||
|
* @param appId 应用编号
|
||||||
|
* @return 渠道列表
|
||||||
|
*/
|
||||||
|
List<PayChannelDO> getChannelListByAppId(Long appId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得指定编号的支付客户端
|
* 获得指定编号的支付客户端
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,11 @@ public class PayChannelServiceImpl implements PayChannelService {
|
||||||
return payChannelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
|
return payChannelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PayChannelDO> getChannelListByAppId(Long appId) {
|
||||||
|
return payChannelMapper.selectListByAppId(appId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PayClient getPayClient(Long id) {
|
public PayClient getPayClient(Long id) {
|
||||||
PayChannelDO channel = validPayChannel(id);
|
PayChannelDO channel = validPayChannel(id);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollStreamUtil;
|
import cn.hutool.core.collection.CollStreamUtil;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
|
@ -10,6 +9,7 @@ import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||||
|
|
@ -120,7 +120,7 @@ public class QiniuSmsClient extends AbstractSmsClient {
|
||||||
.setSuccess("DELIVRD".equals(statusObj.getStr("status"))) // 是否接收成功
|
.setSuccess("DELIVRD".equals(statusObj.getStr("status"))) // 是否接收成功
|
||||||
.setErrorMsg(statusObj.getStr("status")) // 状态报告编码
|
.setErrorMsg(statusObj.getStr("status")) // 状态报告编码
|
||||||
.setMobile(statusObj.getStr("mobile")) // 手机号
|
.setMobile(statusObj.getStr("mobile")) // 手机号
|
||||||
.setReceiveTime(LocalDateTimeUtil.of(statusObj.getLong("delivrd_at") * 1000L)) // 状态报告时间
|
.setReceiveTime(LocalDateTimeUtils.ofEpochSecond(statusObj.getLong("delivrd_at"))) // 状态报告时间
|
||||||
.setSerialNo(statusObj.getStr("message_id")) // 发送序列号
|
.setSerialNo(statusObj.getStr("message_id")) // 发送序列号
|
||||||
.setLogId(statusObj.getLong("seq")); // 用户序列号
|
.setLogId(statusObj.getLong("seq")); // 用户序列号
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -100,11 +99,8 @@ public interface WmsInventoryMapper extends BaseMapperX<WmsInventoryDO> {
|
||||||
if (CollUtil.isEmpty(ids)) {
|
if (CollUtil.isEmpty(ids)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<Long> sortedIds = new ArrayList<>(ids);
|
|
||||||
Collections.sort(sortedIds);
|
|
||||||
return selectList(new LambdaQueryWrapperX<WmsInventoryDO>()
|
return selectList(new LambdaQueryWrapperX<WmsInventoryDO>()
|
||||||
.in(WmsInventoryDO::getId, sortedIds)
|
.in(WmsInventoryDO::getId, ids)
|
||||||
.orderByAsc(WmsInventoryDO::getId)
|
|
||||||
.last("FOR UPDATE"));
|
.last("FOR UPDATE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue