【同步】BOOT 和 CLOUD 的功能
parent
ac1ded1aaf
commit
edb292f622
|
|
@ -1,11 +1,18 @@
|
|||
######################################################################
|
||||
# Build Tools
|
||||
|
||||
# 查看更多 .gitignore 配置 -> https://help.github.com/articles/ignoring-files/
|
||||
.gradle
|
||||
/build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
.flattened-pom.xml
|
||||
|
||||
######################################################################
|
||||
# IDE
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
|
|
@ -13,64 +20,36 @@ target/
|
|||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
*.class
|
||||
target/*
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
/build/
|
||||
nbproject/private/
|
||||
build/*
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
|
||||
|
||||
### 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
|
||||
######################################################################
|
||||
# Others
|
||||
*.log
|
||||
*.xml.versionsBackup
|
||||
*.swp
|
||||
|
||||
functions/mock
|
||||
.temp/**
|
||||
!*/build/*.java
|
||||
!*/build/*.html
|
||||
!*/build/*.xml
|
||||
|
||||
# umi
|
||||
.umi
|
||||
.umi-production
|
||||
### JRebel ###
|
||||
rebel.xml
|
||||
|
||||
# screenshot
|
||||
screenshot
|
||||
.firebase
|
||||
sessionStore
|
||||
outputs/
|
||||
application-my.yaml
|
||||
|
||||
/yudao-ui-app/unpackage/
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1371,6 +1371,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1371,6 +1371,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
|
|
|
|||
|
|
@ -1323,6 +1323,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', to_date('2025-09-06 00:02:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-09-06 00:02:31', 'SYYYY-MM-DD HH24:MI:SS'), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', to_date('2023-11-04 13:05:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:07:16', 'SYYYY-MM-DD HH24:MI:SS'), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', to_date('2025-12-16 19:25:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-12-17 09:46:15', 'SYYYY-MM-DD HH24:MI:SS'), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', SYSDATE, '1', SYSDATE, '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', SYSDATE, '1', SYSDATE, '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', to_date('2026-02-04 00:32:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-02-04 00:32:47', 'SYYYY-MM-DD HH24:MI:SS'), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
|
|
|
|||
|
|
@ -3367,6 +3367,10 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
GO
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3036, 60, N'Admin Uniapp 移动端', N'60', N'infra_codegen_front_type', 0, N'', N'', NULL, N'1', N'2025-12-16 19:25:51', N'1', N'2025-12-17 09:46:15', N'0')
|
||||
GO
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3037, 42, N'Vben5.0 Antdv Next Schema 模版', N'42', N'infra_codegen_front_type', 0, N'', N'', N'', N'1', N'2026-05-16 00:00:00', N'1', N'2026-05-16 00:00:00', N'0')
|
||||
GO
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3038, 43, N'Vben5.0 Antdv Next 标准模版', N'43', N'infra_codegen_front_type', 0, N'', N'', N'', N'1', N'2026-05-16 00:00:00', N'1', N'2026-05-16 00:00:00', N'0')
|
||||
GO
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3040, 1, N'UDP', N'udp', N'iot_protocol_type', 0, N'', N'', N'UDP 协议', N'1', N'2026-02-04 00:32:47', N'1', N'2026-02-04 00:32:47', N'0')
|
||||
GO
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (3041, 2, N'WebSocket', N'websocket', N'iot_protocol_type', 0, N'', N'', N'WebSocket 协议', N'1', N'2026-02-04 00:32:55', N'1', N'2026-02-04 00:32:55', N'0')
|
||||
|
|
|
|||
|
|
@ -57,10 +57,12 @@ docker load -i dm8_20240715_x86_rh6_rq_single.tar
|
|||
```Bash
|
||||
docker compose up -d dm8
|
||||
# 注意:启动完 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
|
||||
```
|
||||
|
||||
> 注意:项目 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 人大金仓
|
||||
|
||||
① 下载人大金仓 Docker 镜像:
|
||||
|
|
|
|||
|
|
@ -871,7 +871,9 @@ class DM8Convertor(Convertor):
|
|||
type = type.lower()
|
||||
|
||||
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"):
|
||||
return "int"
|
||||
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.date.DatePattern;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.date.TemporalAccessorUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
||||
|
|
@ -16,6 +15,7 @@ import java.time.temporal.ChronoUnit;
|
|||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 默认时区
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定时间
|
||||
*
|
||||
|
|
@ -393,7 +419,18 @@ public class LocalDateTimeUtils {
|
|||
* @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常
|
||||
*/
|
||||
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")
|
||||
@Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用")
|
||||
@Parameter(name = "id", description = "流程实例的编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
|
||||
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(
|
||||
@RequestParam(value = "id") String id) {
|
||||
return success(processInstanceService.getProcessInstanceBpmnModelView(id));
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable<Integer> {
|
|||
MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"),
|
||||
POST(22, "岗位"),
|
||||
USER(30, "用户"),
|
||||
APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人
|
||||
APPROVE_USER_SELECT(34, "审批人自选"), // 当前审批人,可在审批时,选择下一个节点的审批人
|
||||
START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人
|
||||
START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景
|
||||
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 起始节点
|
||||
* @return 结果
|
||||
*/
|
||||
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.util.*;
|
||||
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.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
|
|
@ -277,11 +276,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
|||
processVariables.putAll(reqVO.getProcessVariables());
|
||||
}
|
||||
|
||||
// 3. 获取下一个将要执行的节点集合
|
||||
// 3.1 获取下一个将要执行的节点集合
|
||||
FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
|
||||
List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);
|
||||
// 仅仅获取 UserTask 节点 TODO add from jason:如果网关节点和网关节点相连,获取下个 UserTask. 貌似有点不准。
|
||||
List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask);
|
||||
// 3.2 获取 UserTask 节点
|
||||
List<UserTask> nextUserTaskList = BpmnModelUtils.getNextUserTasks(flowElement, bpmnModel, processVariables);
|
||||
List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())
|
||||
.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
|
||||
.setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
|
||||
|
|
|
|||
|
|
@ -669,10 +669,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
}
|
||||
// 1. 获取下一个将要执行的节点集合
|
||||
FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
|
||||
List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
|
||||
List<UserTask> nextFlowNodes = getNextUserTasks(flowElement, bpmnModel, variables);
|
||||
|
||||
// 2. 校验选择的下一个节点的审批人,是否合法
|
||||
for (FlowNode nextFlowNode : nextFlowNodes) {
|
||||
for (UserTask nextFlowNode : nextFlowNodes) {
|
||||
Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
|
||||
// 2.1 情况一:如果节点中的审批人策略为 发起人自选
|
||||
if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
|
||||
|
|
@ -698,8 +698,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
|
||||
// 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空
|
||||
if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
|
||||
// 如果节点存在,但未配置审批人
|
||||
// 特殊:如果当前节点已经存在审批人,则不允许覆盖。 例如并行节点后,设置的审批人自选节点。 https://t.zsxq.com/daxv1
|
||||
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;
|
||||
if (CollUtil.isEmpty(assignees)) {
|
||||
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_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_CONTACT_CUSTOMER_NOT_MATCH = new ErrorCode(1_020_002_004, "商机关联的联系人不属于该客户");
|
||||
|
||||
// ========== 联系人管理 1-020-003-000 ==========
|
||||
ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
|
||||
|
|
|
|||
|
|
@ -138,6 +138,26 @@ public class CrmBusinessController {
|
|||
.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")
|
||||
@Operation(summary = "获得商机分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||
|
|
|
|||
|
|
@ -114,6 +114,17 @@ public class CrmContactController {
|
|||
.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")
|
||||
@Operation(summary = "获得联系人分页")
|
||||
@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.contact.CrmContactDO;
|
||||
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.contact.CrmContactService;
|
||||
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.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
|
@ -27,10 +30,13 @@ import org.springframework.web.bind.annotation.*;
|
|||
import java.util.ArrayList;
|
||||
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.util.collection.CollectionUtils.convertSet;
|
||||
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.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
|
||||
|
||||
|
||||
@Tag(name = "管理后台 - 跟进记录")
|
||||
|
|
@ -45,6 +51,8 @@ public class CrmFollowUpRecordController {
|
|||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
|
@ -68,6 +76,13 @@ public class CrmFollowUpRecordController {
|
|||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam("id") Long 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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
|
|||
.eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
|
||||
}
|
||||
|
||||
default List<CrmBusinessDO> selectListByCustomerId(Long customerId) {
|
||||
return selectList(CrmBusinessDO::getCustomerId, customerId);
|
||||
}
|
||||
|
||||
default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {
|
||||
return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||
.in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())
|
||||
|
|
|
|||
|
|
@ -195,6 +195,22 @@ public interface CrmBusinessService {
|
|||
*/
|
||||
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.ListUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
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.CrmBusinessStatusDO;
|
||||
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.CrmBusinessProductMapper;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
|
|
@ -194,9 +196,15 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
|||
if (saveReqVO.getCustomerId() != null) {
|
||||
customerService.validateCustomer(saveReqVO.getCustomerId());
|
||||
}
|
||||
// 校验联系人
|
||||
// 校验联系人(且必须与商机属于同一客户,避免跨客户关联)
|
||||
if (saveReqVO.getContactId() != null) {
|
||||
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) {
|
||||
|
|
@ -377,6 +385,24 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
|||
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
|
||||
public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {
|
||||
return businessMapper.selectPage(pageVO);
|
||||
|
|
|
|||
|
|
@ -169,4 +169,12 @@ public interface CrmContactService {
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
@Operation(summary = "创建销售订单")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:create')")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-order:create')")
|
||||
public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) {
|
||||
return success(saleOrderService.createSaleOrder(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新销售订单")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:update')")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-order:update')")
|
||||
public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) {
|
||||
saleOrderService.updateSaleOrder(updateReqVO);
|
||||
return success(true);
|
||||
|
|
@ -76,7 +76,7 @@ public class ErpSaleOrderController {
|
|||
|
||||
@PutMapping("/update-status")
|
||||
@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,
|
||||
@RequestParam("status") Integer status) {
|
||||
saleOrderService.updateSaleOrderStatus(id, status);
|
||||
|
|
@ -86,7 +86,7 @@ public class ErpSaleOrderController {
|
|||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除销售订单")
|
||||
@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) {
|
||||
saleOrderService.deleteSaleOrder(ids);
|
||||
return success(true);
|
||||
|
|
@ -95,7 +95,7 @@ public class ErpSaleOrderController {
|
|||
@GetMapping("/get")
|
||||
@Operation(summary = "获得销售订单")
|
||||
@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) {
|
||||
ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
|
||||
if (saleOrder == null) {
|
||||
|
|
@ -115,7 +115,7 @@ public class ErpSaleOrderController {
|
|||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得销售订单分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:query')")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-order:query')")
|
||||
public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) {
|
||||
PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
|
||||
return success(buildSaleOrderVOPageResult(pageResult));
|
||||
|
|
@ -123,7 +123,7 @@ public class ErpSaleOrderController {
|
|||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出销售订单 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-out:export')")
|
||||
@PreAuthorize("@ss.hasPermission('erp:sale-order:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
|
@ -161,4 +161,4 @@ public class ErpSaleOrderController {
|
|||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,49 +40,13 @@
|
|||
/>
|
||||
</view>
|
||||
#elseif ($column.htmlType == "datetime" && $listOperationCondition == "BETWEEN")
|
||||
#set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})
|
||||
<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>
|
||||
<yd-search-date-range v-model="formData.${javaField}" label="${comment}" />
|
||||
#elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
${comment}
|
||||
</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>
|
||||
|
|
@ -236,29 +200,6 @@ const placeholder = computed(() => {
|
|||
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() {
|
||||
visible.value = false
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@
|
|||
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
||||
import { onUnload } from '@dcloudio/uni-app'
|
||||
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 { useAccess } from '@/hooks/useAccess'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
#if ($hasDict == 1)
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
#end
|
||||
|
|
@ -133,9 +133,7 @@ function handleDelete() {
|
|||
await delete${simpleClassName}(props.id)
|
||||
toast.success('删除成功')
|
||||
uni.$emit('${table.moduleName}:${table.businessName}:reload')
|
||||
setTimeout(() => {
|
||||
handleBack()
|
||||
}, 500)
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<!-- 表单区域 -->
|
||||
<view>
|
||||
<wd-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||
<wd-cell-group border>
|
||||
#foreach($column in $columns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey)
|
||||
|
|
@ -25,10 +25,23 @@
|
|||
#elseif ($javaType == "Boolean")
|
||||
#set ($dictMethod = "getBoolDictOptions")
|
||||
#end
|
||||
## 优先判断是否有字典,有字典则使用 radio-group
|
||||
#if (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
|
||||
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
|
||||
<wd-radio-group v-model="formData.${javaField}" shape="button">
|
||||
## 下拉选择(字典)→ yd-form-picker,内部按 dictType 解析选项,适合选项较多
|
||||
#if ($column.htmlType == "select" && $dictType && "" != $dictType)
|
||||
<yd-form-picker
|
||||
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
|
||||
v-for="dict in $dictMethod(DICT_TYPE.${dictType.toUpperCase()})"
|
||||
:key="dict.value"
|
||||
|
|
@ -37,54 +50,78 @@
|
|||
{{ dict.label }}
|
||||
</wd-radio>
|
||||
</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")
|
||||
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
|
||||
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||
<wd-input-number
|
||||
v-model="formData.${javaField}"
|
||||
#if (${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal")
|
||||
:precision="2"
|
||||
input-type="number"
|
||||
#end
|
||||
:min="0"
|
||||
/>
|
||||
</wd-cell>
|
||||
</wd-form-item>
|
||||
## 布尔类型
|
||||
#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-cell>
|
||||
</wd-form-item>
|
||||
## 日期时间类型
|
||||
#elseif (${javaType.toLowerCase()} == "date" || ${javaType.toLowerCase()} == "localdate" || ${javaType.toLowerCase()} == "localdatetime")
|
||||
#set ($pickerType = "date")
|
||||
#if (${javaType.toLowerCase()} == "localdatetime")
|
||||
#set ($pickerType = "datetime")
|
||||
#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
|
||||
v-model="formData.${javaField}"
|
||||
v-model:visible="${javaField}Visible"
|
||||
type="${pickerType}"
|
||||
label="${comment}"
|
||||
label-width="180rpx"
|
||||
prop="${javaField}"
|
||||
/>
|
||||
## 文本域
|
||||
#elseif ($column.htmlType == "textarea")
|
||||
<wd-textarea
|
||||
v-model="formData.${javaField}"
|
||||
label="${comment}"
|
||||
label-width="180rpx"
|
||||
placeholder="请输入${comment}"
|
||||
:maxlength="200"
|
||||
show-word-limit
|
||||
clearable
|
||||
/>
|
||||
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||
<wd-textarea
|
||||
v-model="formData.${javaField}"
|
||||
clearable
|
||||
placeholder="请输入${comment}"
|
||||
:maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
</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
|
||||
<wd-input
|
||||
v-model="formData.${javaField}"
|
||||
label="${comment}"
|
||||
label-width="180rpx"
|
||||
prop="${javaField}"
|
||||
clearable
|
||||
placeholder="请输入${comment}"
|
||||
/>
|
||||
<wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
|
||||
<wd-input
|
||||
v-model="formData.${javaField}"
|
||||
clearable
|
||||
placeholder="请输入${comment}"
|
||||
/>
|
||||
</wd-form-item>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
|
@ -107,7 +144,7 @@
|
|||
</template>
|
||||
|
||||
<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())
|
||||
#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
|
||||
#set ($primaryTsType = "number")
|
||||
|
|
@ -115,6 +152,8 @@ import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
|
|||
#set ($primaryTsType = "string")
|
||||
#end
|
||||
#set ($hasDict = 0)
|
||||
#set ($hasDate = 0)
|
||||
#set ($hasDateTime = 0)
|
||||
#set ($hasGetDictOptions = 0)
|
||||
#set ($hasGetIntDictOptions = 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.dictType && "" != $column.dictType)
|
||||
#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)
|
||||
#elseif ($column.javaType == "String")
|
||||
#elseif ($column.htmlType == "radio" && $column.javaType == "String")
|
||||
#set ($hasGetStrDictOptions = 1)
|
||||
#elseif ($column.javaType == "Boolean")
|
||||
#elseif ($column.htmlType == "radio" && $column.javaType == "Boolean")
|
||||
#set ($hasGetBoolDictOptions = 1)
|
||||
#else
|
||||
#elseif ($column.htmlType == "radio")
|
||||
#set ($hasGetDictOptions = 1)
|
||||
#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
|
||||
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
|
||||
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}'
|
||||
#if ($hasDict == 1)
|
||||
#if ($hasGetDictOptions == 1 || $hasGetIntDictOptions == 1 || $hasGetStrDictOptions == 1 || $hasGetBoolDictOptions == 1)
|
||||
#set ($dictImportNames = "")
|
||||
#if ($hasGetDictOptions == 1)
|
||||
#set ($dictImportNames = "${dictImportNames}getDictOptions, ")
|
||||
|
|
@ -157,10 +204,23 @@ import { create${simpleClassName}, get${simpleClassName}, update${simpleClassNam
|
|||
#set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1))
|
||||
import { $dictImportNames } from '@/hooks/useDict'
|
||||
#end
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
#if ($hasDict == 1)
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
#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<{
|
||||
id?: ${primaryTsType} | any
|
||||
|
|
@ -196,7 +256,12 @@ const formData = ref<${simpleClassName}>({
|
|||
#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)
|
||||
#set ($javaFieldLower = $column.javaField.toLowerCase())
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey
|
||||
|
|
@ -204,7 +269,7 @@ const formRules = {
|
|||
${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }],
|
||||
#end
|
||||
#end
|
||||
} // 表单校验规则
|
||||
}) // 表单校验规则
|
||||
const formRef = ref<FormInstance>() // 表单组件引用
|
||||
|
||||
/** 返回上一页 */
|
||||
|
|
@ -237,9 +302,7 @@ async function handleSubmit() {
|
|||
toast.success('新增成功')
|
||||
}
|
||||
uni.$emit('${table.moduleName}:${table.businessName}:reload')
|
||||
setTimeout(() => {
|
||||
handleBack()
|
||||
}, 500)
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,10 +152,21 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
|
|||
protected void writeResult(Map<String, String> result, String basePath) {
|
||||
// 写入文件内容
|
||||
List<Map<String, String>> asserts = new ArrayList<>();
|
||||
Set<String> usedContentPaths = new LinkedHashSet<>();
|
||||
result.forEach((filePath, fileContent) -> {
|
||||
String lastFilePath = StrUtil.subAfter(filePath, '/', true);
|
||||
String contentPath = StrUtil.subAfter(lastFilePath, '.', true)
|
||||
+ '/' + StrUtil.subBefore(lastFilePath, '.', true);
|
||||
String ext = StrUtil.subAfter(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)
|
||||
.put("contentPath", contentPath).build());
|
||||
FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath);
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@
|
|||
import type { Student } from '@/api/infra/demo'
|
||||
import { onUnload } from '@dcloudio/uni-app'
|
||||
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 { useAccess } from '@/hooks/useAccess'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
|
||||
|
|
@ -116,9 +116,7 @@ function handleDelete() {
|
|||
await deleteStudent(props.id)
|
||||
toast.success('删除成功')
|
||||
uni.$emit('infra:demo:reload')
|
||||
setTimeout(() => {
|
||||
handleBack()
|
||||
}, 500)
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,45 +9,48 @@
|
|||
|
||||
<!-- 表单区域 -->
|
||||
<view>
|
||||
<wd-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
label="名字"
|
||||
label-width="180rpx"
|
||||
prop="name"
|
||||
clearable
|
||||
placeholder="请输入名字"
|
||||
/>
|
||||
<wd-textarea
|
||||
v-model="formData.description"
|
||||
label="简介"
|
||||
label-width="180rpx"
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
show-word-limit
|
||||
clearable
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model="formData.birthday"
|
||||
type="datetime"
|
||||
label="出生日期"
|
||||
label-width="180rpx"
|
||||
<wd-form-item title="名字" title-width="180rpx" prop="name">
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
clearable
|
||||
placeholder="请输入名字"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="简介" title-width="180rpx" prop="description">
|
||||
<wd-textarea
|
||||
v-model="formData.description"
|
||||
clearable
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="出生日期"
|
||||
title-width="180rpx"
|
||||
prop="birthday"
|
||||
is-link
|
||||
:value="formatDateTime(formData.birthday)"
|
||||
placeholder="请选择出生日期"
|
||||
@click="birthdayVisible = true"
|
||||
/>
|
||||
<wd-cell title="性别" title-width="180rpx" prop="sex" center>
|
||||
<wd-radio-group v-model="formData.sex" shape="button">
|
||||
<wd-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-cell>
|
||||
<wd-cell title="是否有效" title-width="180rpx" prop="enabled" center>
|
||||
<wd-radio-group v-model="formData.enabled" shape="button">
|
||||
<wd-datetime-picker
|
||||
v-model="formData.birthday"
|
||||
v-model:visible="birthdayVisible"
|
||||
type="datetime"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="formData.sex"
|
||||
label="性别"
|
||||
label-width="180rpx"
|
||||
prop="sex"
|
||||
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
<wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center>
|
||||
<wd-radio-group v-model="formData.enabled" type="button">
|
||||
<wd-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
|
|
@ -56,31 +59,20 @@
|
|||
{{ dict.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-cell>
|
||||
<wd-input
|
||||
v-model="formData.avatar"
|
||||
label="头像"
|
||||
label-width="180rpx"
|
||||
prop="avatar"
|
||||
clearable
|
||||
placeholder="请输入头像"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.video"
|
||||
label="附件"
|
||||
label-width="180rpx"
|
||||
prop="video"
|
||||
clearable
|
||||
placeholder="请输入附件"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.memo"
|
||||
label="备注"
|
||||
label-width="180rpx"
|
||||
prop="memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="头像" title-width="180rpx" prop="avatar">
|
||||
<yd-upload-img v-model="formData.avatar" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="附件" title-width="180rpx" prop="video">
|
||||
<yd-upload-file v-model="formData.video" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="备注" title-width="180rpx" prop="memo">
|
||||
<wd-input
|
||||
v-model="formData.memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
/>
|
||||
</wd-form-item>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
</view>
|
||||
|
|
@ -100,14 +92,16 @@
|
|||
</template>
|
||||
|
||||
<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 { 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 { getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import { createFormSchema } from '@/utils/wot'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number | any
|
||||
|
|
@ -134,7 +128,8 @@ const formData = ref<Student>({
|
|||
video: '',
|
||||
memo: '',
|
||||
}) // 表单数据
|
||||
const formRules = {
|
||||
const birthdayVisible = ref(false) // 出生日期选择器状态
|
||||
const formSchema = createFormSchema({
|
||||
name: [{ required: true, message: '名字不能为空' }],
|
||||
description: [{ required: true, message: '简介不能为空' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空' }],
|
||||
|
|
@ -143,7 +138,7 @@ const formRules = {
|
|||
avatar: [{ required: true, message: '头像不能为空' }],
|
||||
video: [{ required: true, message: '附件不能为空' }],
|
||||
memo: [{ required: true, message: '备注不能为空' }],
|
||||
} // 表单校验规则
|
||||
}) // 表单校验规则
|
||||
const formRef = ref<FormInstance>() // 表单组件引用
|
||||
|
||||
/** 返回上一页 */
|
||||
|
|
@ -176,9 +171,7 @@ async function handleSubmit() {
|
|||
toast.success('新增成功')
|
||||
}
|
||||
uni.$emit('infra:demo:reload')
|
||||
setTimeout(() => {
|
||||
handleBack()
|
||||
}, 500)
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
<view class="yd-search-form-label">
|
||||
性别
|
||||
</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>
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
<view class="yd-search-form-label">
|
||||
是否有效
|
||||
</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>
|
||||
|
|
@ -61,42 +61,7 @@
|
|||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</view>
|
||||
<view class="yd-search-form-item">
|
||||
<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>
|
||||
<yd-search-date-range v-model="formData.createTime" label="创建时间" />
|
||||
<view class="yd-search-form-actions">
|
||||
<wd-button class="flex-1" plain @click="handleReset">
|
||||
重置
|
||||
|
|
@ -151,21 +116,6 @@ const placeholder = computed(() => {
|
|||
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() {
|
||||
visible.value = false
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
package cn.iocoder.yudao.module.iot.framework.tdengine.core;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* TDEngine 表字段
|
||||
*/
|
||||
|
|
@ -61,4 +64,15 @@ public class TDengineTableField {
|
|||
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) {
|
||||
return convertList(thingModels, thingModel -> {
|
||||
TDengineTableField field = new TDengineTableField(
|
||||
StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写
|
||||
TDengineTableField.buildFieldName(thingModel.getIdentifier()),
|
||||
TYPE_MAPPING.get(thingModel.getProperty().getDataType()));
|
||||
String dataType = thingModel.getProperty().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.text.CharPool;
|
||||
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.service.rule.scene.matcher.condition.IotCurrentTimeConditionMatcher;
|
||||
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.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ public class IotSceneRuleTimeHelper {
|
|||
LocalDateTime now = LocalDateTime.now();
|
||||
if (isDateTimeOperator(operatorEnum)) {
|
||||
// 日期时间匹配(时间戳,秒级)
|
||||
long currentTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond();
|
||||
long currentTimestamp = LocalDateTimeUtils.toEpochSecond(now);
|
||||
return matchDateTime(currentTimestamp, operatorEnum, param);
|
||||
} else {
|
||||
// 当日时间匹配(HH:mm:ss)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
TAGS ('${device.id}')
|
||||
(ts, report_time,
|
||||
<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>
|
||||
)
|
||||
VALUES
|
||||
|
|
@ -68,12 +68,12 @@
|
|||
|
||||
<select id="selectListByHistory"
|
||||
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}
|
||||
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 ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])}
|
||||
ORDER BY ts DESC
|
||||
</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.dal.dataobject.device.IotDeviceDO;
|
||||
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.model.ThingModelProperty;
|
||||
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.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 org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
|
@ -17,8 +21,12 @@ import org.mockito.InjectMocks;
|
|||
import org.mockito.Mock;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
|
@ -38,6 +46,8 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
|||
@Mock
|
||||
private IotThingModelService thingModelService;
|
||||
@Mock
|
||||
private IotProductService productService;
|
||||
@Mock
|
||||
private IotDevicePropertyMapper devicePropertyMapper;
|
||||
@Mock
|
||||
private DevicePropertyRedisDAO deviceDataRedisDAO;
|
||||
|
|
@ -100,12 +110,15 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
|||
params.put("Temperature", "abc");
|
||||
IotDeviceMessage message = buildMessage(params);
|
||||
|
||||
// mock 方法
|
||||
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
||||
.thenReturn(singletonList(temperature));
|
||||
when(thingModelService.convertThingModelPropertyValue(temperature, "abc")).thenReturn(null);
|
||||
|
||||
// 调用,并断言:不会抛出异常
|
||||
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
||||
|
||||
// 断言:没有合法属性,不会写入 TDengine 与 Redis
|
||||
verify(devicePropertyMapper, never()).insert(any(), any(), anyLong(), anyLong());
|
||||
verify(deviceDataRedisDAO, never()).putAll(anyLong(), any());
|
||||
}
|
||||
|
|
@ -119,11 +132,14 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
|||
params.put("Temperature", null);
|
||||
IotDeviceMessage message = buildMessage(params);
|
||||
|
||||
// mock 方法
|
||||
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
||||
.thenReturn(singletonList(thingModel));
|
||||
|
||||
// 调用,并断言:不会抛出异常
|
||||
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
||||
|
||||
// 断言:跳过空值,不会转换属性值,也不会写入 TDengine 与 Redis
|
||||
verify(thingModelService, never()).convertThingModelPropertyValue(any(), any());
|
||||
verify(devicePropertyMapper, never()).insert(any(), any(), anyLong(), anyLong());
|
||||
verify(deviceDataRedisDAO, never()).putAll(anyLong(), any());
|
||||
|
|
@ -139,17 +155,48 @@ public class IotDevicePropertyServiceImplTest extends BaseMockitoUnitTest {
|
|||
params.put("PowerSwitch", true);
|
||||
IotDeviceMessage message = buildMessage(params);
|
||||
|
||||
// mock 方法
|
||||
when(thingModelService.getThingModelListByProductIdFromCache(device.getProductId()))
|
||||
.thenReturn(singletonList(thingModel));
|
||||
when(thingModelService.convertThingModelPropertyValue(thingModel, true)).thenReturn((byte) 1);
|
||||
|
||||
// 调用,并断言:非字符串 key 不影响其它合法属性
|
||||
assertDoesNotThrow(() -> service.saveDeviceProperty(device, message));
|
||||
|
||||
// 断言:只写入合法属性
|
||||
Map<String, Object> dbProperties = captureMapperInsertProperties();
|
||||
assertEquals(1, dbProperties.size());
|
||||
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.service.comment.ProductCommentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
|
|
@ -34,6 +35,15 @@ public class ProductCommentController {
|
|||
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")
|
||||
@Operation(summary = "显示 / 隐藏评论")
|
||||
@PreAuthorize("@ss.hasPermission('product:comment:update')")
|
||||
|
|
|
|||
|
|
@ -118,10 +118,10 @@ public class ProductSpuController {
|
|||
}
|
||||
|
||||
@GetMapping("/get-count")
|
||||
@Operation(summary = "获得商品 SPU 分页 tab count")
|
||||
@Operation(summary = "获得商品 SPU 分页 tab count(支持按 name/categoryId/createTime 筛选)")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||
public CommonResult<Map<Integer, Long>> getSpuCount() {
|
||||
return success(productSpuService.getTabsCount());
|
||||
public CommonResult<Map<Integer, Long>> getSpuCount(ProductSpuPageReqVO reqVO) {
|
||||
return success(productSpuService.getTabsCount(reqVO));
|
||||
}
|
||||
|
||||
@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 浏览量
|
||||
*
|
||||
|
|
|
|||
|
|
@ -52,6 +52,14 @@ public interface ProductCommentService {
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProductCommentDO getComment(Long id) {
|
||||
return validateCommentExists(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) {
|
||||
return productCommentMapper.selectPage(pageReqVO);
|
||||
|
|
|
|||
|
|
@ -118,11 +118,12 @@ public interface ProductSpuService {
|
|||
void updateSpuStatus(ProductSpuUpdateStatusReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 获取 SPU 列表标签对应的 Count 数量
|
||||
* 获取 SPU 列表标签对应的 Count 数量(支持按 name/categoryId/createTime 筛选)
|
||||
*
|
||||
* @param reqVO 筛选条件
|
||||
* @return Count 数量
|
||||
*/
|
||||
Map<Integer, Long> getTabsCount();
|
||||
Map<Integer, Long> getTabsCount(ProductSpuPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 通过分类 categoryId 查询 SPU 个数
|
||||
|
|
|
|||
|
|
@ -256,23 +256,19 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, Long> getTabsCount() {
|
||||
public Map<Integer, Long> getTabsCount(ProductSpuPageReqVO reqVO) {
|
||||
Map<Integer, Long> counts = Maps.newLinkedHashMapWithExpectedSize(5);
|
||||
// 查询销售中的商品数量
|
||||
// 每个 tab 的数量 = 筛选条件(name/categoryId/createTime)+ 该 tab 的状态/库存条件
|
||||
counts.put(ProductSpuPageReqVO.FOR_SALE,
|
||||
productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()));
|
||||
// 查询仓库中的商品数量
|
||||
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.FOR_SALE));
|
||||
counts.put(ProductSpuPageReqVO.IN_WAREHOUSE,
|
||||
productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.DISABLE.getStatus()));
|
||||
// 查询售空的商品数量
|
||||
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.IN_WAREHOUSE));
|
||||
counts.put(ProductSpuPageReqVO.SOLD_OUT,
|
||||
productSpuMapper.selectCount(ProductSpuDO::getStock, 0));
|
||||
// 查询触发警戒库存的商品数量
|
||||
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.SOLD_OUT));
|
||||
counts.put(ProductSpuPageReqVO.ALERT_STOCK,
|
||||
productSpuMapper.selectCount());
|
||||
// 查询回收站中的商品数量
|
||||
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.ALERT_STOCK));
|
||||
counts.put(ProductSpuPageReqVO.RECYCLE_BIN,
|
||||
productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));
|
||||
productSpuMapper.selectCountByTab(reqVO, ProductSpuPageReqVO.RECYCLE_BIN));
|
||||
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.PageResult;
|
||||
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.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.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.convert.coupon.CouponConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
|
|
@ -15,12 +17,12 @@ import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
|||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
|
@ -63,6 +65,15 @@ public class CouponController {
|
|||
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")
|
||||
@Operation(summary = "发送优惠劵")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:coupon:send')")
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ public interface CouponService {
|
|||
Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates);
|
||||
|
||||
/**
|
||||
* 获得优惠劵
|
||||
* 【会员】获得优惠劵
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param id 编号
|
||||
|
|
@ -168,4 +168,12 @@ public interface CouponService {
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CouponDO getCoupon(Long id) {
|
||||
return couponMapper.selectById(id);
|
||||
}
|
||||
|
||||
private CouponDO validateCouponExists(Long id) {
|
||||
CouponDO coupon = couponMapper.selectById(id);
|
||||
if (coupon == null) {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public class DeliveryPickUpStoreController {
|
|||
}
|
||||
List<AdminUserRespDTO> verifyUsers = CollUtil.isNotEmpty(deliveryPickUpStore.getVerifyUserIds()) ?
|
||||
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)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ public class DeliveryPickUpStoreRespVO extends DeliveryPickUpStoreBaseVO {
|
|||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "地区名称", example = "上海上海市普陀区")
|
||||
private String areaName;
|
||||
|
||||
@Schema(description = "核销用户数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<UserSimpleBaseVO> verifyUsers;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ public interface DeliveryPickUpStoreConvert {
|
|||
|
||||
PageResult<DeliveryPickUpStoreRespVO> convertPage(PageResult<DeliveryPickUpStoreDO> page);
|
||||
|
||||
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||
DeliveryPickUpStoreRespVO convert01(DeliveryPickUpStoreDO bean);
|
||||
|
||||
List<DeliveryPickUpStoreSimpleRespVO> convertList1(List<DeliveryPickUpStoreDO> list);
|
||||
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||
DeliveryPickUpStoreSimpleRespVO convert02(DeliveryPickUpStoreDO bean);
|
||||
|
|
|
|||
|
|
@ -16,4 +16,7 @@ public class AddressRespVO extends AddressBaseVO {
|
|||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "地区名称", example = "上海上海市普陀区")
|
||||
private String areaName;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ public interface AddressConvert {
|
|||
return AreaUtils.format(areaId);
|
||||
}
|
||||
|
||||
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||
AddressRespVO convert03(MemberAddressDO bean);
|
||||
|
||||
List<AddressRespVO> convertList2(List<MemberAddressDO> list);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public class SubscribeHandler implements WxMpMessageHandler {
|
|||
} catch (WxErrorException e) {
|
||||
log.error("[handle][粉丝({})] 获取粉丝信息失败!", wxMessage.getFromUser(), e);
|
||||
// 特殊情况(个人账号,无接口权限):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.setOpenId(wxMessage.getFromUser());
|
||||
wxMpUser.setSubscribe(true);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,15 @@ public class PayChannelController {
|
|||
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")
|
||||
@Operation(summary = "获得指定应用的开启的支付渠道编码列表")
|
||||
@Parameter(name = "appId", description = "应用编号", required = true, example = "1")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.yudao.module.pay.convert.channel;
|
||||
|
||||
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.PayChannelRespVO;
|
||||
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.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
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()))")
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,4 +28,8 @@ public interface PayChannelMapper extends BaseMapperX<PayChannelDO> {
|
|||
.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);
|
||||
|
||||
/**
|
||||
* 获得指定应用的渠道列表
|
||||
*
|
||||
* @param appId 应用编号
|
||||
* @return 渠道列表
|
||||
*/
|
||||
List<PayChannelDO> getChannelListByAppId(Long appId);
|
||||
|
||||
/**
|
||||
* 获得指定编号的支付客户端
|
||||
*
|
||||
|
|
|
|||
|
|
@ -156,6 +156,11 @@ public class PayChannelServiceImpl implements PayChannelService {
|
|||
return payChannelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PayChannelDO> getChannelListByAppId(Long appId) {
|
||||
return payChannelMapper.selectListByAppId(appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayClient getPayClient(Long 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.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
|
|
@ -10,6 +9,7 @@ import cn.hutool.crypto.digest.HmacAlgorithm;
|
|||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
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.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||
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"))) // 是否接收成功
|
||||
.setErrorMsg(statusObj.getStr("status")) // 状态报告编码
|
||||
.setMobile(statusObj.getStr("mobile")) // 手机号
|
||||
.setReceiveTime(LocalDateTimeUtil.of(statusObj.getLong("delivrd_at") * 1000L)) // 状态报告时间
|
||||
.setReceiveTime(LocalDateTimeUtils.ofEpochSecond(statusObj.getLong("delivrd_at"))) // 状态报告时间
|
||||
.setSerialNo(statusObj.getStr("message_id")) // 发送序列号
|
||||
.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 java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -100,11 +99,8 @@ public interface WmsInventoryMapper extends BaseMapperX<WmsInventoryDO> {
|
|||
if (CollUtil.isEmpty(ids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Long> sortedIds = new ArrayList<>(ids);
|
||||
Collections.sort(sortedIds);
|
||||
return selectList(new LambdaQueryWrapperX<WmsInventoryDO>()
|
||||
.in(WmsInventoryDO::getId, sortedIds)
|
||||
.orderByAsc(WmsInventoryDO::getId)
|
||||
.in(WmsInventoryDO::getId, ids)
|
||||
.last("FOR UPDATE"));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue