diff --git a/.gitignore b/.gitignore index a38cc562d..cfc97c4f4 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ Servers .metadata upload gen_code +litemall-wx/project.config.json diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/BeanUtil.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/BeanUtil.java index 4f2dceb58..7bdbe3029 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/BeanUtil.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/BeanUtil.java @@ -5,19 +5,41 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; +/** + * Spring 容器工具类,用于在非 Spring 管理的类中获取 Bean 实例。 + */ @Component public class BeanUtil implements ApplicationContextAware { + /** 保存 Spring 容器上下文 */ protected static ApplicationContext context; + /** + * 注入 Spring 容器上下文。 + * + * @param applicationContext Spring 容器上下文 + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } + /** + * 根据 Bean 名称获取 Bean 实例。 + * + * @param name Bean 名称 + * @return Bean 实例 + */ public static Object getBean(String name) { return context.getBean(name); } + /** + * 根据 Bean 类型获取 Bean 实例。 + * + * @param c Bean 类型 + * @param Bean 类型泛型 + * @return Bean 实例 + */ public static T getBean(Class c){ return context.getBean(c); } diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/CharUtil.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/CharUtil.java index 628989bc4..84353049f 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/CharUtil.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/CharUtil.java @@ -2,12 +2,23 @@ import java.util.Random; +/** + * 随机字符串工具类。 + */ public class CharUtil { + /** + * 生成指定长度的随机字符串(包含小写字母和数字)。 + * + * @param num 随机字符串的长度 + * @return 随机字符串 + */ public static String getRandomString(Integer num) { + /* 随机字符池:小写字母与数字 */ String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); + /* 循环从字符池中随机取 num 个字符拼接 */ for (int i = 0; i < num; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); @@ -15,10 +26,18 @@ public static String getRandomString(Integer num) { return sb.toString(); } + /** + * 生成指定长度的随机数字字符串。 + * + * @param num 随机字符串的长度 + * @return 随机数字字符串 + */ public static String getRandomNum(Integer num) { + /* 随机字符池:仅数字 */ String base = "0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); + /* 循环从数字池中随机取 num 个字符拼接 */ for (int i = 0; i < num; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/DateTimeUtil.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/DateTimeUtil.java index 99764b978..63da7daec 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/DateTimeUtil.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/DateTimeUtil.java @@ -9,10 +9,10 @@ public class DateTimeUtil { /** - * 格式 yyyy年MM月dd日 HH:mm:ss + * 将日期时间格式化为 "yyyy年MM月dd日 HH:mm:ss" 形式的字符串。 * - * @param dateTime - * @return + * @param dateTime 待格式化的日期时间 + * @return 格式化后的日期时间字符串 */ public static String getDateTimeDisplayString(LocalDateTime dateTime) { DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/HttpUtil.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/HttpUtil.java index 60713df1e..116bcfcf2 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/HttpUtil.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/HttpUtil.java @@ -11,11 +11,11 @@ import java.net.URL; import java.util.Map; + /** - * 向指定 URL 发送POST方法的请求 - * - * @return 远程资源的响应结果 + * HTTP 工具类,用于向指定 URL 发送 POST 请求。be'a */ + public class HttpUtil { private static final Log logger = LogFactory.getLog(HttpUtil.class); @@ -27,7 +27,6 @@ public class HttpUtil { * @param params 请求的参数集合 * @return 远程资源的响应结果 */ - @SuppressWarnings("unused") public static String sendPost(String url, Map params) { OutputStreamWriter out = null; BufferedReader in = null; @@ -35,21 +34,21 @@ public static String sendPost(String url, Map params) { try { URL realUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); - // 发送POST请求必须设置如下两行 + /*发送POST请求必须设置如下两行*/ conn.setDoOutput(true); conn.setDoInput(true); - // POST方法 + /* POST方法 */ conn.setRequestMethod("POST"); - // 设置通用的请求属性 + /* 设置通用的请求属性 */ conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.connect(); - // 获取URLConnection对象对应的输出流 + /* 获取URLConnection对象对应的输出流 */ out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8"); - // 发送请求参数 + /* 发送请求参数 */ if (params != null) { StringBuilder param = new StringBuilder(); for (Map.Entry entry : params.entrySet()) { @@ -64,9 +63,9 @@ public static String sendPost(String url, Map params) { out.write(param.toString()); } - // flush输出流的缓冲 + /* flush输出流的缓冲 */ out.flush(); - // 定义BufferedReader输入流来读取URL的响应 + /* 定义BufferedReader输入流来读取URL的响应 */ in = new BufferedReader( new InputStreamReader(conn.getInputStream(), "UTF-8")); String line; @@ -76,7 +75,7 @@ public static String sendPost(String url, Map params) { } catch (Exception e) { logger.error(e.getMessage(), e); } - //使用finally块来关闭输出流、输入流 + /* 使用finally块来关闭输出流、输入流 */ finally { try { if (out != null) { diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/IpUtil.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/IpUtil.java index f753428e7..46e6ff2ab 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/IpUtil.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/IpUtil.java @@ -14,6 +14,15 @@ public class IpUtil { private static final Log logger = LogFactory.getLog(IpUtil.class); + /** + * 获取客户端真实 IP 地址。 + *

+ * 依次从请求头 x-forwarded-for、Proxy-Client-IP、WL-Proxy-Client-IP 中获取, + * 若都为空则回退到远程地址;经过多级代理时取第一个 IP。 + * + * @param request HTTP 请求 + * @return 客户端 IP 地址 + */ public static String getIpAddr(HttpServletRequest request) { String ipAddress; try { @@ -27,7 +36,7 @@ public static String getIpAddr(HttpServletRequest request) { if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); if (ipAddress.equals("127.0.0.1")) { - // 根据网卡取本机配置的IP + /* 根据网卡取本机配置的IP */ InetAddress inet = null; try { inet = InetAddress.getLocalHost(); @@ -37,9 +46,9 @@ public static String getIpAddr(HttpServletRequest request) { ipAddress = inet.getHostAddress(); } } - // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 - if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length() - // = 15 + /* 通过多个代理时,第一个 IP 为客户端真实 IP,多个 IP 以逗号分隔; */ + /* 单个 IP 最长 15 个字符(如 255.255.255.255),超过 15 说明包含多个 IP */ + if (ipAddress != null && ipAddress.length() > 15) { if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); } diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/JacksonUtil.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/JacksonUtil.java index c64f9ff6d..7fd036f68 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/JacksonUtil.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/JacksonUtil.java @@ -11,15 +11,27 @@ import java.util.List; import java.util.Map; +/** + * JSON 解析与序列化工具类,基于 Jackson 实现。 + */ public class JacksonUtil { private static final Log logger = LogFactory.getLog(JacksonUtil.class); + /** + * 解析 JSON 字符串中指定字段的字符串值。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @return 字段对应的字符串值,不存在或解析失败时返回 null + */ public static String parseString(String body, String field) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); + /* 获取指定字段对应的节点 */ JsonNode leaf = node.get(field); if (leaf != null) return leaf.asText(); @@ -29,15 +41,23 @@ public static String parseString(String body, String field) { return null; } - + /** + * 解析 JSON 字符串中指定字段的字符串列表。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @return 字段对应的字符串列表,不存在或解析失败时返回 null + */ public static List parseStringList(String body, String field) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); JsonNode leaf = node.get(field); if (leaf != null) + /* 将字段节点转换为字符串列表 */ return mapper.convertValue(leaf, new TypeReference>() { }); } catch (IOException e) { @@ -46,11 +66,20 @@ public static List parseStringList(String body, String field) { return null; } + /** + * 解析 JSON 字符串中指定字段的整数值。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @return 字段对应的整数值,不存在或解析失败时返回 null + */ public static Integer parseInteger(String body, String field) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); + /* 获取指定字段对应的节点 */ JsonNode leaf = node.get(field); if (leaf != null) return leaf.asInt(); @@ -60,14 +89,23 @@ public static Integer parseInteger(String body, String field) { return null; } + /** + * 解析 JSON 字符串中指定字段的整数列表。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @return 字段对应的整数列表,不存在或解析失败时返回 null + */ public static List parseIntegerList(String body, String field) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); JsonNode leaf = node.get(field); if (leaf != null) + /* 将字段节点转换为整数列表 */ return mapper.convertValue(leaf, new TypeReference>() { }); } catch (IOException e) { @@ -76,12 +114,20 @@ public static List parseIntegerList(String body, String field) { return null; } - + /** + * 解析 JSON 字符串中指定字段的布尔值。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @return 字段对应的布尔值,不存在或解析失败时返回 null + */ public static Boolean parseBoolean(String body, String field) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); + /* 获取指定字段对应的节点 */ JsonNode leaf = node.get(field); if (leaf != null) return leaf.asBoolean(); @@ -91,13 +137,22 @@ public static Boolean parseBoolean(String body, String field) { return null; } + /** + * 解析 JSON 字符串中指定字段的短整数值。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @return 字段对应的短整数值,不存在或解析失败时返回 null + */ public static Short parseShort(String body, String field) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); JsonNode leaf = node.get(field); if (leaf != null) { + /* 先按 int 取值再强转为 short */ Integer value = leaf.asInt(); return value.shortValue(); } @@ -107,13 +162,22 @@ public static Short parseShort(String body, String field) { return null; } + /** + * 解析 JSON 字符串中指定字段的字节值。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @return 字段对应的字节值,不存在或解析失败时返回 null + */ public static Byte parseByte(String body, String field) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); JsonNode leaf = node.get(field); if (leaf != null) { + /* 先按 int 取值再强转为 byte */ Integer value = leaf.asInt(); return value.byteValue(); } @@ -123,12 +187,23 @@ public static Byte parseByte(String body, String field) { return null; } + /** + * 解析 JSON 字符串中指定字段为指定类型的对象。 + * + * @param body JSON 字符串 + * @param field 字段名 + * @param clazz 目标类型 + * @param 目标类型泛型 + * @return 字段对应的对象,不存在或解析失败时返回 null + */ public static T parseObject(String body, String field, Class clazz) { ObjectMapper mapper = new ObjectMapper(); JsonNode node; try { + /* 将 JSON 字符串解析为树形结构 */ node = mapper.readTree(body); node = node.get(field); + /* 将字段节点转换为指定类型的对象 */ return mapper.treeToValue(node, clazz); } catch (IOException e) { logger.error(e.getMessage(), e); @@ -136,13 +211,19 @@ public static T parseObject(String body, String field, Class clazz) { return null; } + /** + * 将 JSON 字符串解析为 JSON 节点。 + * + * @param json JSON 字符串 + * @return JSON 节点,解析失败时返回 null + */ public static Object toNode(String json) { if (json == null) { return null; } ObjectMapper mapper = new ObjectMapper(); try { - + /* 解析 JSON 字符串为 JSON 节点 */ return mapper.readTree(json); } catch (IOException e) { logger.error(e.getMessage(), e); @@ -151,9 +232,16 @@ public static Object toNode(String json) { return null; } + /** + * 将 JSON 字符串解析为 Map。 + * + * @param data JSON 字符串 + * @return Map 对象,解析失败时返回 null + */ public static Map toMap(String data) { ObjectMapper objectMapper = new ObjectMapper(); try { + /* 将 JSON 字符串反序列化为 Map */ return objectMapper.readValue(data, new TypeReference>() { }); } catch (IOException e) { @@ -162,12 +250,19 @@ public static Map toMap(String data) { return null; } + /** + * 将对象序列化为 JSON 字符串。 + * + * @param data 待序列化的对象 + * @return JSON 字符串,序列化失败时返回 null + */ public static String toJson(Object data) { ObjectMapper objectMapper = new ObjectMapper(); try { + /* 将对象序列化为 JSON 字符串 */ return objectMapper.writeValueAsString(data); } catch (JsonProcessingException e) { - e.printStackTrace(); + logger.error(e.getMessage(), e); } return null; } diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/ResponseUtil.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/ResponseUtil.java index c7a3521af..db21e87e4 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/ResponseUtil.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/ResponseUtil.java @@ -40,6 +40,11 @@ * */ public class ResponseUtil { + /** + * 构建成功响应。 + * + * @return 成功响应(不含数据) + */ public static Object ok() { Map obj = new HashMap(); obj.put("errno", 0); @@ -47,6 +52,12 @@ public static Object ok() { return obj; } + /** + * 构建成功响应。 + * + * @param data 响应数据 + * @return 成功响应(含数据) + */ public static Object ok(Object data) { Map obj = new HashMap(); obj.put("errno", 0); @@ -55,10 +66,17 @@ public static Object ok(Object data) { return obj; } + /** + * 构建分页列表成功响应。 + * + * @param list 列表数据(Page 类型时自动附带分页信息) + * @return 成功响应(含分页数据) + */ public static Object okList(List list) { Map data = new HashMap(); data.put("list", list); + /* PageHelper 分页对象会附带分页信息,否则按单页处理 */ if (list instanceof Page) { Page page = (Page) list; data.put("total", page.getTotal()); @@ -75,10 +93,18 @@ public static Object okList(List list) { return ok(data); } + /** + * 构建分页列表成功响应。 + * + * @param list 列表数据 + * @param pagedList 用于分页信息的分页对象 + * @return 成功响应(含分页数据) + */ public static Object okList(List list, List pagedList) { Map data = new HashMap(); data.put("list", list); + /* PageHelper 分页对象会附带分页信息,否则按单页处理 */ if (pagedList instanceof Page) { Page page = (Page) pagedList; data.put("total", page.getTotal()); @@ -95,6 +121,11 @@ public static Object okList(List list, List pagedList) { return ok(data); } + /** + * 构建失败响应。 + * + * @return 失败响应(错误码 -1) + */ public static Object fail() { Map obj = new HashMap(); obj.put("errno", -1); @@ -102,6 +133,13 @@ public static Object fail() { return obj; } + /** + * 构建失败响应。 + * + * @param errno 错误码 + * @param errmsg 错误消息 + * @return 失败响应 + */ public static Object fail(int errno, String errmsg) { Map obj = new HashMap(); obj.put("errno", errno); @@ -109,6 +147,14 @@ public static Object fail(int errno, String errmsg) { return obj; } + /** + * 构建带数据的失败响应。 + * + * @param errno 错误码 + * @param errmsg 错误消息 + * @param data 响应数据 + * @return 失败响应(含数据) + */ public static Object fail(int errno, String errmsg, String data) { Map obj = new HashMap(3); obj.put("errno", errno); @@ -117,36 +163,75 @@ public static Object fail(int errno, String errmsg, String data) { return obj; } + /** + * 构建参数错误响应(401)。 + * + * @return 参数错误响应 + */ public static Object badArgument() { return fail(401, "参数不对"); } + /** + * 构建参数值错误响应(402)。 + * + * @return 参数值错误响应 + */ public static Object badArgumentValue() { return fail(402, "参数值不对"); } + /** + * 构建未登录响应(501)。 + * + * @return 未登录响应 + */ public static Object unlogin() { return fail(501, "请登录"); } + /** + * 构建系统内部错误响应(502)。 + * + * @return 系统内部错误响应 + */ public static Object serious() { return fail(502, "系统内部错误"); } + /** + * 构建业务不支持响应(503)。 + * + * @return 业务不支持响应 + */ public static Object unsupport() { return fail(503, "业务不支持"); } + /** + * 构建更新数据失效响应(504)。 + * + * @return 更新数据失效响应 + */ public static Object updatedDateExpired() { return fail(504, "更新数据已经失效"); } + /** + * 构建更新数据失败响应(505)。 + * + * @return 更新数据失败响应 + */ public static Object updatedDataFailed() { return fail(505, "更新数据失败"); } + /** + * 构建无操作权限响应(506)。 + * + * @return 无操作权限响应 + */ public static Object unauthz() { return fail(506, "无操作权限"); } } - diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/SystemInfoPrinter.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/SystemInfoPrinter.java index 51a02f642..b90cba435 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/util/SystemInfoPrinter.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/util/SystemInfoPrinter.java @@ -5,16 +5,30 @@ import java.util.Map; +/** + * 系统信息打印工具类。 + *

+ * 用于在应用启动时以统一格式将系统配置信息打印到日志。 + */ public class SystemInfoPrinter { private static final Log logger = LogFactory.getLog(SystemInfoPrinter.class); public static final String CREATE_PART_COPPER = "XOXOXOXOX"; + /** 打印行的最大宽度,用于生成对齐的分隔横线 */ private static int maxSize = 0; + /** + * 打印系统信息。 + * + * @param title 信息标题 + * @param infos 键值对形式的信息集合 + */ public static void printInfo(String title, Map infos) { + /* 先根据内容计算最大行宽 */ setMaxSize(infos); + /* 依次打印头部、各信息行、结尾 */ printHeader(title); for (Map.Entry entry : infos.entrySet()) { @@ -24,6 +38,11 @@ public static void printInfo(String title, Map infos) { printEnd(); } + /** + * 计算打印每行内容的最大宽度。 + * + * @param infos 键值对形式的信息集合 + */ private static void setMaxSize(Map infos) { for (Map.Entry entry : infos.entrySet()) { if (entry.getValue() == null) @@ -35,9 +54,15 @@ private static void setMaxSize(Map infos) { maxSize = size; } + /* 额外留 30 个字符的空隙,保证横线不紧贴内容 */ maxSize = maxSize + 30; } + /** + * 打印信息头部。 + * + * @param title 信息标题 + */ private static void printHeader(String title) { logger.info(getLineCopper()); logger.info(""); @@ -45,11 +70,19 @@ private static void printHeader(String title) { logger.info(""); } + /** + * 打印信息结尾。 + */ private static void printEnd() { logger.info(" "); logger.info(getLineCopper()); } + /** + * 生成一条由等号组成的横线。 + * + * @return 横线字符串 + */ private static String getLineCopper() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < maxSize; i++) { @@ -59,15 +92,23 @@ private static String getLineCopper() { return sb.toString(); } + /** + * 打印一行信息。 + * + * @param head 键 + * @param line 值 + */ private static void printLine(String head, String line) { if (line == null) return; + /* 分区标题(以特殊前缀开头)单独高亮打印 */ if (head.startsWith(CREATE_PART_COPPER)) { logger.info(""); logger.info(" [[ " + line + " ]]"); logger.info(""); } else { + /* 普通键值对:key -> value */ logger.info(" " + head + " -> " + line); } } diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Order.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Order.java index dfbaa2b35..4e25178fa 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Order.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Order.java @@ -9,6 +9,11 @@ import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; +/** + * 排序类型校验注解。 + *

+ * 用于校验请求参数中的排序方式是否在允许范围内(默认仅支持 asc 和 desc)。 + */ @Target({METHOD, FIELD, PARAMETER}) @Retention(RUNTIME) @Documented diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/OrderValidator.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/OrderValidator.java index b163a117b..cbf722f16 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/OrderValidator.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/OrderValidator.java @@ -6,12 +6,16 @@ import javax.validation.ConstraintValidatorContext; import java.util.List; +/** + * {@link Order} 注解的校验器,校验排序方式是否在允许范围内。 + */ public class OrderValidator implements ConstraintValidator { private List valueList; @Override public void initialize(Order order) { valueList = Lists.newArrayList(); + /* 将允许值统一转为大写,便于校验时忽略大小写 */ for (String val : order.accepts()) { valueList.add(val.toUpperCase()); } @@ -19,9 +23,7 @@ public void initialize(Order order) { @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { - if (!valueList.contains(s.toUpperCase())) { - return false; - } - return true; + /* 校验时忽略大小写 */ + return valueList.contains(s.toUpperCase()); } } diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Sort.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Sort.java index 2568e7099..17d8300a0 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Sort.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/Sort.java @@ -9,6 +9,11 @@ import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; +/** + * 排序字段校验注解。 + *

+ * 用于校验请求参数中的排序字段是否在允许范围内(默认仅支持 add_time 和 id)。 + */ @Target({METHOD, FIELD, PARAMETER}) @Retention(RUNTIME) @Documented diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/SortValidator.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/SortValidator.java index eca179bba..2ee14afbc 100644 --- a/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/SortValidator.java +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/validator/SortValidator.java @@ -6,12 +6,16 @@ import javax.validation.ConstraintValidatorContext; import java.util.List; +/** + * {@link Sort} 注解的校验器,校验排序字段是否在允许范围内。 + */ public class SortValidator implements ConstraintValidator { private List valueList; @Override public void initialize(Sort sort) { valueList = Lists.newArrayList(); + /* 将允许值统一转为大写,便于校验时忽略大小写 */ for (String val : sort.accepts()) { valueList.add(val.toUpperCase()); } @@ -19,9 +23,7 @@ public void initialize(Sort sort) { @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { - if (!valueList.contains(s.toUpperCase())) { - return false; - } - return true; + /* 校验时忽略大小写 */ + return valueList.contains(s.toUpperCase()); } } diff --git a/litemall-vue/package.json b/litemall-vue/package.json index e9b816307..5f2bc1b37 100644 --- a/litemall-vue/package.json +++ b/litemall-vue/package.json @@ -19,6 +19,7 @@ "dayjs": "^1.7.7", "js-cookie": "2.2.0", "lodash": "^4.17.21", + "regenerator-runtime": "^0.14.1", "vant": "^2.0.6", "vue": "^2.5.17", "vue-router": "^3.0.1", diff --git a/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java index 4ea16adcc..2f0ead825 100644 --- a/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java +++ b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java @@ -659,6 +659,53 @@ public Object prepay(Integer userId, String body, HttpServletRequest request) { return ResponseUtil.ok(result); } + /** + * 模拟支付(本地开发/演示用,无需真实微信支付资质) + *

+ * 直接标记订单为已支付,跳过微信支付的统一下单和回调流程。 + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 操作结果 + */ + @Transactional + public Object mockPay(Integer userId, String body) { + if (userId == null) { + return ResponseUtil.unlogin(); + } + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + if (orderId == null) { + return ResponseUtil.badArgument(); + } + + LitemallOrder order = orderService.findById(userId, orderId); + if (order == null) { + return ResponseUtil.badArgumentValue(); + } + if (!order.getUserId().equals(userId)) { + return ResponseUtil.badArgumentValue(); + } + + // 检测是否能够支付 + OrderHandleOption handleOption = OrderUtil.build(order); + if (!handleOption.isPay()) { + return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能支付"); + } + + // 模拟支付成功:直接标记订单为已支付 + order.setOrderStatus(OrderUtil.STATUS_PAY); + order.setPayTime(LocalDateTime.now()); + order.setPayId("MOCK" + order.getOrderSn()); + if (orderService.updateWithOptimisticLocker(order) == 0) { + return ResponseUtil.updatedDateExpired(); + } + + // 取消订单超时未支付任务 + taskService.removeTask(new OrderUnpaidTask(order.getId())); + + return ResponseUtil.ok(); + } + /** * 微信H5支付 * diff --git a/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java index 230a259f9..945e0126b 100644 --- a/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java +++ b/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxOrderController.java @@ -92,6 +92,18 @@ public Object prepay(@LoginUser Integer userId, @RequestBody String body, HttpSe return wxOrderService.prepay(userId, body, request); } + /** + * 模拟支付(本地开发/演示用) + * + * @param userId 用户ID + * @param body 订单信息,{ orderId:xxx } + * @return 操作结果 + */ + @PostMapping("mock-pay") + public Object mockPay(@LoginUser Integer userId, @RequestBody String body) { + return wxOrderService.mockPay(userId, body); + } + /** * 微信H5支付 * @param userId diff --git a/litemall-wx/config/api.js b/litemall-wx/config/api.js index 3d9ebbfc4..d956e15ff 100644 --- a/litemall-wx/config/api.js +++ b/litemall-wx/config/api.js @@ -68,6 +68,7 @@ module.exports = { OrderSubmit: WxApiRoot + 'order/submit', // 提交订单 OrderPrepay: WxApiRoot + 'order/prepay', // 订单的预支付会话 + OrderMockPay: WxApiRoot + 'order/mock-pay', // 模拟支付(本地演示用) OrderList: WxApiRoot + 'order/list', //订单列表 OrderDetail: WxApiRoot + 'order/detail', //订单详情 OrderCancel: WxApiRoot + 'order/cancel', //取消订单 diff --git a/litemall-wx/lib/vant-weapp/icon/index.wxss b/litemall-wx/lib/vant-weapp/icon/index.wxss index 8fa5efcd0..59983ae3d 100644 --- a/litemall-wx/lib/vant-weapp/icon/index.wxss +++ b/litemall-wx/lib/vant-weapp/icon/index.wxss @@ -1 +1 @@ -@import '../common/index.wxss';@font-face{font-weight:400;font-family:vant-icon;font-style:normal;font-display:auto;src:url(https://img.yzcdn.cn/vant/vant-icon-0bc654.woff2) format("woff2"),url(https://img.yzcdn.cn/vant/vant-icon-0bc654.woff) format("woff"),url(https://img.yzcdn.cn/vant/vant-icon-0bc654.ttf) format("truetype")}.van-icon{position:relative;font:normal normal normal 14px/1 vant-icon;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased}.van-icon,.van-icon:before{display:inline-block}.van-icon-add-o:before{content:"\F000"}.van-icon-add-square:before{content:"\F001"}.van-icon-add:before{content:"\F002"}.van-icon-after-sale:before{content:"\F003"}.van-icon-aim:before{content:"\F004"}.van-icon-alipay:before{content:"\F005"}.van-icon-apps-o:before{content:"\F006"}.van-icon-arrow-down:before{content:"\F007"}.van-icon-arrow-left:before{content:"\F008"}.van-icon-arrow-up:before{content:"\F009"}.van-icon-arrow:before{content:"\F00A"}.van-icon-ascending:before{content:"\F00B"}.van-icon-audio:before{content:"\F00C"}.van-icon-award-o:before{content:"\F00D"}.van-icon-award:before{content:"\F00E"}.van-icon-bag-o:before{content:"\F00F"}.van-icon-bag:before{content:"\F010"}.van-icon-balance-list-o:before{content:"\F011"}.van-icon-balance-list:before{content:"\F012"}.van-icon-balance-o:before{content:"\F013"}.van-icon-balance-pay:before{content:"\F014"}.van-icon-bar-chart-o:before{content:"\F015"}.van-icon-bars:before{content:"\F016"}.van-icon-bell:before{content:"\F017"}.van-icon-bill-o:before{content:"\F018"}.van-icon-bill:before{content:"\F019"}.van-icon-birthday-cake-o:before{content:"\F01A"}.van-icon-bookmark-o:before{content:"\F01B"}.van-icon-bookmark:before{content:"\F01C"}.van-icon-browsing-history-o:before{content:"\F01D"}.van-icon-browsing-history:before{content:"\F01E"}.van-icon-brush-o:before{content:"\F01F"}.van-icon-bulb-o:before{content:"\F020"}.van-icon-bullhorn-o:before{content:"\F021"}.van-icon-calender-o:before{content:"\F022"}.van-icon-card:before{content:"\F023"}.van-icon-cart-circle-o:before{content:"\F024"}.van-icon-cart-circle:before{content:"\F025"}.van-icon-cart-o:before{content:"\F026"}.van-icon-cart:before{content:"\F027"}.van-icon-cash-back-record:before{content:"\F028"}.van-icon-cash-on-deliver:before{content:"\F029"}.van-icon-cashier-o:before{content:"\F02A"}.van-icon-certificate:before{content:"\F02B"}.van-icon-chart-trending-o:before{content:"\F02C"}.van-icon-chat-o:before{content:"\F02D"}.van-icon-chat:before{content:"\F02E"}.van-icon-checked:before{content:"\F02F"}.van-icon-circle:before{content:"\F030"}.van-icon-clear:before{content:"\F031"}.van-icon-clock-o:before{content:"\F032"}.van-icon-clock:before{content:"\F033"}.van-icon-close:before{content:"\F034"}.van-icon-closed-eye:before{content:"\F035"}.van-icon-cluster-o:before{content:"\F036"}.van-icon-cluster:before{content:"\F037"}.van-icon-column:before{content:"\F038"}.van-icon-comment-circle-o:before{content:"\F039"}.van-icon-comment-circle:before{content:"\F03A"}.van-icon-comment-o:before{content:"\F03B"}.van-icon-comment:before{content:"\F03C"}.van-icon-completed:before{content:"\F03D"}.van-icon-contact:before{content:"\F03E"}.van-icon-coupon-o:before{content:"\F03F"}.van-icon-coupon:before{content:"\F040"}.van-icon-credit-pay:before{content:"\F041"}.van-icon-cross:before{content:"\F042"}.van-icon-debit-pay:before{content:"\F043"}.van-icon-delete:before{content:"\F044"}.van-icon-descending:before{content:"\F045"}.van-icon-description:before{content:"\F046"}.van-icon-desktop-o:before{content:"\F047"}.van-icon-diamond-o:before{content:"\F048"}.van-icon-diamond:before{content:"\F049"}.van-icon-discount:before{content:"\F04A"}.van-icon-down:before{content:"\F04B"}.van-icon-ecard-pay:before{content:"\F04C"}.van-icon-edit:before{content:"\F04D"}.van-icon-ellipsis:before{content:"\F04E"}.van-icon-empty:before{content:"\F04F"}.van-icon-envelop-o:before{content:"\F050"}.van-icon-exchange:before{content:"\F051"}.van-icon-expand-o:before{content:"\F052"}.van-icon-expand:before{content:"\F053"}.van-icon-eye-o:before{content:"\F054"}.van-icon-eye:before{content:"\F055"}.van-icon-fail:before{content:"\F056"}.van-icon-failure:before{content:"\F057"}.van-icon-filter-o:before{content:"\F058"}.van-icon-fire-o:before{content:"\F059"}.van-icon-fire:before{content:"\F05A"}.van-icon-flag-o:before{content:"\F05B"}.van-icon-flower-o:before{content:"\F05C"}.van-icon-free-postage:before{content:"\F05D"}.van-icon-friends-o:before{content:"\F05E"}.van-icon-friends:before{content:"\F05F"}.van-icon-gem-o:before{content:"\F060"}.van-icon-gem:before{content:"\F061"}.van-icon-gift-card-o:before{content:"\F062"}.van-icon-gift-card:before{content:"\F063"}.van-icon-gift-o:before{content:"\F064"}.van-icon-gift:before{content:"\F065"}.van-icon-gold-coin-o:before{content:"\F066"}.van-icon-gold-coin:before{content:"\F067"}.van-icon-good-job-o:before{content:"\F068"}.van-icon-good-job:before{content:"\F069"}.van-icon-goods-collect-o:before{content:"\F06A"}.van-icon-goods-collect:before{content:"\F06B"}.van-icon-graphic:before{content:"\F06C"}.van-icon-home-o:before{content:"\F06D"}.van-icon-hot-o:before{content:"\F06E"}.van-icon-hot-sale-o:before{content:"\F06F"}.van-icon-hot-sale:before{content:"\F070"}.van-icon-hot:before{content:"\F071"}.van-icon-hotel-o:before{content:"\F072"}.van-icon-idcard:before{content:"\F073"}.van-icon-info-o:before{content:"\F074"}.van-icon-info:before{content:"\F075"}.van-icon-invition:before{content:"\F076"}.van-icon-label-o:before{content:"\F077"}.van-icon-label:before{content:"\F078"}.van-icon-like-o:before{content:"\F079"}.van-icon-like:before{content:"\F07A"}.van-icon-live:before{content:"\F07B"}.van-icon-location-o:before{content:"\F07C"}.van-icon-location:before{content:"\F07D"}.van-icon-lock:before{content:"\F07E"}.van-icon-logistics:before{content:"\F07F"}.van-icon-manager-o:before{content:"\F080"}.van-icon-manager:before{content:"\F081"}.van-icon-map-marked:before{content:"\F082"}.van-icon-medel-o:before{content:"\F083"}.van-icon-medel:before{content:"\F084"}.van-icon-more-o:before{content:"\F085"}.van-icon-more:before{content:"\F086"}.van-icon-music-o:before{content:"\F087"}.van-icon-music:before{content:"\F088"}.van-icon-new-arrival-o:before{content:"\F089"}.van-icon-new-arrival:before{content:"\F08A"}.van-icon-new-o:before{content:"\F08B"}.van-icon-new:before{content:"\F08C"}.van-icon-newspaper-o:before{content:"\F08D"}.van-icon-notes-o:before{content:"\F08E"}.van-icon-orders-o:before{content:"\F08F"}.van-icon-other-pay:before{content:"\F090"}.van-icon-paid:before{content:"\F091"}.van-icon-passed:before{content:"\F092"}.van-icon-pause-circle-o:before{content:"\F093"}.van-icon-pause-circle:before{content:"\F094"}.van-icon-pause:before{content:"\F095"}.van-icon-peer-pay:before{content:"\F096"}.van-icon-pending-payment:before{content:"\F097"}.van-icon-phone-circle-o:before{content:"\F098"}.van-icon-phone-circle:before{content:"\F099"}.van-icon-phone-o:before{content:"\F09A"}.van-icon-phone:before{content:"\F09B"}.van-icon-photo-o:before{content:"\F09C"}.van-icon-photo:before{content:"\F09D"}.van-icon-photograph:before{content:"\F09E"}.van-icon-play-circle-o:before{content:"\F09F"}.van-icon-play-circle:before{content:"\F0A0"}.van-icon-play:before{content:"\F0A1"}.van-icon-plus:before{content:"\F0A2"}.van-icon-point-gift-o:before{content:"\F0A3"}.van-icon-point-gift:before{content:"\F0A4"}.van-icon-points:before{content:"\F0A5"}.van-icon-printer:before{content:"\F0A6"}.van-icon-qr-invalid:before{content:"\F0A7"}.van-icon-qr:before{content:"\F0A8"}.van-icon-question-o:before{content:"\F0A9"}.van-icon-question:before{content:"\F0AA"}.van-icon-records:before{content:"\F0AB"}.van-icon-refund-o:before{content:"\F0AC"}.van-icon-replay:before{content:"\F0AD"}.van-icon-scan:before{content:"\F0AE"}.van-icon-search:before{content:"\F0AF"}.van-icon-send-gift-o:before{content:"\F0B0"}.van-icon-send-gift:before{content:"\F0B1"}.van-icon-service-o:before{content:"\F0B2"}.van-icon-service:before{content:"\F0B3"}.van-icon-setting-o:before{content:"\F0B4"}.van-icon-setting:before{content:"\F0B5"}.van-icon-share:before{content:"\F0B6"}.van-icon-shop-collect-o:before{content:"\F0B7"}.van-icon-shop-collect:before{content:"\F0B8"}.van-icon-shop-o:before{content:"\F0B9"}.van-icon-shop:before{content:"\F0BA"}.van-icon-shopping-cart-o:before{content:"\F0BB"}.van-icon-shopping-cart:before{content:"\F0BC"}.van-icon-shrink:before{content:"\F0BD"}.van-icon-sign:before{content:"\F0BE"}.van-icon-smile-comment-o:before{content:"\F0BF"}.van-icon-smile-comment:before{content:"\F0C0"}.van-icon-smile-o:before{content:"\F0C1"}.van-icon-smile:before{content:"\F0C2"}.van-icon-star-o:before{content:"\F0C3"}.van-icon-star:before{content:"\F0C4"}.van-icon-stop-circle-o:before{content:"\F0C5"}.van-icon-stop-circle:before{content:"\F0C6"}.van-icon-stop:before{content:"\F0C7"}.van-icon-success:before{content:"\F0C8"}.van-icon-thumb-circle-o:before{content:"\F0C9"}.van-icon-thumb-circle:before{content:"\F0CA"}.van-icon-todo-list-o:before{content:"\F0CB"}.van-icon-todo-list:before{content:"\F0CC"}.van-icon-tosend:before{content:"\F0CD"}.van-icon-tv-o:before{content:"\F0CE"}.van-icon-umbrella-circle:before{content:"\F0CF"}.van-icon-underway-o:before{content:"\F0D0"}.van-icon-underway:before{content:"\F0D1"}.van-icon-upgrade:before{content:"\F0D2"}.van-icon-user-circle-o:before{content:"\F0D3"}.van-icon-user-o:before{content:"\F0D4"}.van-icon-video-o:before{content:"\F0D5"}.van-icon-video:before{content:"\F0D6"}.van-icon-vip-card-o:before{content:"\F0D7"}.van-icon-vip-card:before{content:"\F0D8"}.van-icon-volume-o:before{content:"\F0D9"}.van-icon-volume:before{content:"\F0DA"}.van-icon-wap-home-o:before{content:"\F0DB"}.van-icon-wap-home:before{content:"\F0DC"}.van-icon-wap-nav:before{content:"\F0DD"}.van-icon-warn-o:before{content:"\F0DE"}.van-icon-warning-o:before{content:"\F0DF"}.van-icon-warning:before{content:"\F0E0"}.van-icon-weapp-nav:before{content:"\F0E1"}.van-icon-wechat:before{content:"\F0E2"}.van-icon-youzan-shield:before{content:"\F0E3"}:host{display:-webkit-inline-flex;display:inline-flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center}.van-icon--image{width:1em;height:1em}.van-icon__image{width:100%;height:100%}.van-icon__info{z-index:1} \ No newline at end of file +@import '../common/index.wxss';@font-face{font-weight:400;font-family:vant-icon;font-style:normal;font-display:auto;src:url(data:font/woff2;base64,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) format("woff2")}.van-icon{position:relative;font:normal normal normal 14px/1 vant-icon;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased}.van-icon,.van-icon:before{display:inline-block}.van-icon-add-o:before{content:"\F000"}.van-icon-add-square:before{content:"\F001"}.van-icon-add:before{content:"\F002"}.van-icon-after-sale:before{content:"\F003"}.van-icon-aim:before{content:"\F004"}.van-icon-alipay:before{content:"\F005"}.van-icon-apps-o:before{content:"\F006"}.van-icon-arrow-down:before{content:"\F007"}.van-icon-arrow-left:before{content:"\F008"}.van-icon-arrow-up:before{content:"\F009"}.van-icon-arrow:before{content:"\F00A"}.van-icon-ascending:before{content:"\F00B"}.van-icon-audio:before{content:"\F00C"}.van-icon-award-o:before{content:"\F00D"}.van-icon-award:before{content:"\F00E"}.van-icon-bag-o:before{content:"\F00F"}.van-icon-bag:before{content:"\F010"}.van-icon-balance-list-o:before{content:"\F011"}.van-icon-balance-list:before{content:"\F012"}.van-icon-balance-o:before{content:"\F013"}.van-icon-balance-pay:before{content:"\F014"}.van-icon-bar-chart-o:before{content:"\F015"}.van-icon-bars:before{content:"\F016"}.van-icon-bell:before{content:"\F017"}.van-icon-bill-o:before{content:"\F018"}.van-icon-bill:before{content:"\F019"}.van-icon-birthday-cake-o:before{content:"\F01A"}.van-icon-bookmark-o:before{content:"\F01B"}.van-icon-bookmark:before{content:"\F01C"}.van-icon-browsing-history-o:before{content:"\F01D"}.van-icon-browsing-history:before{content:"\F01E"}.van-icon-brush-o:before{content:"\F01F"}.van-icon-bulb-o:before{content:"\F020"}.van-icon-bullhorn-o:before{content:"\F021"}.van-icon-calender-o:before{content:"\F022"}.van-icon-card:before{content:"\F023"}.van-icon-cart-circle-o:before{content:"\F024"}.van-icon-cart-circle:before{content:"\F025"}.van-icon-cart-o:before{content:"\F026"}.van-icon-cart:before{content:"\F027"}.van-icon-cash-back-record:before{content:"\F028"}.van-icon-cash-on-deliver:before{content:"\F029"}.van-icon-cashier-o:before{content:"\F02A"}.van-icon-certificate:before{content:"\F02B"}.van-icon-chart-trending-o:before{content:"\F02C"}.van-icon-chat-o:before{content:"\F02D"}.van-icon-chat:before{content:"\F02E"}.van-icon-checked:before{content:"\F02F"}.van-icon-circle:before{content:"\F030"}.van-icon-clear:before{content:"\F031"}.van-icon-clock-o:before{content:"\F032"}.van-icon-clock:before{content:"\F033"}.van-icon-close:before{content:"\F034"}.van-icon-closed-eye:before{content:"\F035"}.van-icon-cluster-o:before{content:"\F036"}.van-icon-cluster:before{content:"\F037"}.van-icon-column:before{content:"\F038"}.van-icon-comment-circle-o:before{content:"\F039"}.van-icon-comment-circle:before{content:"\F03A"}.van-icon-comment-o:before{content:"\F03B"}.van-icon-comment:before{content:"\F03C"}.van-icon-completed:before{content:"\F03D"}.van-icon-contact:before{content:"\F03E"}.van-icon-coupon-o:before{content:"\F03F"}.van-icon-coupon:before{content:"\F040"}.van-icon-credit-pay:before{content:"\F041"}.van-icon-cross:before{content:"\F042"}.van-icon-debit-pay:before{content:"\F043"}.van-icon-delete:before{content:"\F044"}.van-icon-descending:before{content:"\F045"}.van-icon-description:before{content:"\F046"}.van-icon-desktop-o:before{content:"\F047"}.van-icon-diamond-o:before{content:"\F048"}.van-icon-diamond:before{content:"\F049"}.van-icon-discount:before{content:"\F04A"}.van-icon-down:before{content:"\F04B"}.van-icon-ecard-pay:before{content:"\F04C"}.van-icon-edit:before{content:"\F04D"}.van-icon-ellipsis:before{content:"\F04E"}.van-icon-empty:before{content:"\F04F"}.van-icon-envelop-o:before{content:"\F050"}.van-icon-exchange:before{content:"\F051"}.van-icon-expand-o:before{content:"\F052"}.van-icon-expand:before{content:"\F053"}.van-icon-eye-o:before{content:"\F054"}.van-icon-eye:before{content:"\F055"}.van-icon-fail:before{content:"\F056"}.van-icon-failure:before{content:"\F057"}.van-icon-filter-o:before{content:"\F058"}.van-icon-fire-o:before{content:"\F059"}.van-icon-fire:before{content:"\F05A"}.van-icon-flag-o:before{content:"\F05B"}.van-icon-flower-o:before{content:"\F05C"}.van-icon-free-postage:before{content:"\F05D"}.van-icon-friends-o:before{content:"\F05E"}.van-icon-friends:before{content:"\F05F"}.van-icon-gem-o:before{content:"\F060"}.van-icon-gem:before{content:"\F061"}.van-icon-gift-card-o:before{content:"\F062"}.van-icon-gift-card:before{content:"\F063"}.van-icon-gift-o:before{content:"\F064"}.van-icon-gift:before{content:"\F065"}.van-icon-gold-coin-o:before{content:"\F066"}.van-icon-gold-coin:before{content:"\F067"}.van-icon-good-job-o:before{content:"\F068"}.van-icon-good-job:before{content:"\F069"}.van-icon-goods-collect-o:before{content:"\F06A"}.van-icon-goods-collect:before{content:"\F06B"}.van-icon-graphic:before{content:"\F06C"}.van-icon-home-o:before{content:"\F06D"}.van-icon-hot-o:before{content:"\F06E"}.van-icon-hot-sale-o:before{content:"\F06F"}.van-icon-hot-sale:before{content:"\F070"}.van-icon-hot:before{content:"\F071"}.van-icon-hotel-o:before{content:"\F072"}.van-icon-idcard:before{content:"\F073"}.van-icon-info-o:before{content:"\F074"}.van-icon-info:before{content:"\F075"}.van-icon-invition:before{content:"\F076"}.van-icon-label-o:before{content:"\F077"}.van-icon-label:before{content:"\F078"}.van-icon-like-o:before{content:"\F079"}.van-icon-like:before{content:"\F07A"}.van-icon-live:before{content:"\F07B"}.van-icon-location-o:before{content:"\F07C"}.van-icon-location:before{content:"\F07D"}.van-icon-lock:before{content:"\F07E"}.van-icon-logistics:before{content:"\F07F"}.van-icon-manager-o:before{content:"\F080"}.van-icon-manager:before{content:"\F081"}.van-icon-map-marked:before{content:"\F082"}.van-icon-medel-o:before{content:"\F083"}.van-icon-medel:before{content:"\F084"}.van-icon-more-o:before{content:"\F085"}.van-icon-more:before{content:"\F086"}.van-icon-music-o:before{content:"\F087"}.van-icon-music:before{content:"\F088"}.van-icon-new-arrival-o:before{content:"\F089"}.van-icon-new-arrival:before{content:"\F08A"}.van-icon-new-o:before{content:"\F08B"}.van-icon-new:before{content:"\F08C"}.van-icon-newspaper-o:before{content:"\F08D"}.van-icon-notes-o:before{content:"\F08E"}.van-icon-orders-o:before{content:"\F08F"}.van-icon-other-pay:before{content:"\F090"}.van-icon-paid:before{content:"\F091"}.van-icon-passed:before{content:"\F092"}.van-icon-pause-circle-o:before{content:"\F093"}.van-icon-pause-circle:before{content:"\F094"}.van-icon-pause:before{content:"\F095"}.van-icon-peer-pay:before{content:"\F096"}.van-icon-pending-payment:before{content:"\F097"}.van-icon-phone-circle-o:before{content:"\F098"}.van-icon-phone-circle:before{content:"\F099"}.van-icon-phone-o:before{content:"\F09A"}.van-icon-phone:before{content:"\F09B"}.van-icon-photo-o:before{content:"\F09C"}.van-icon-photo:before{content:"\F09D"}.van-icon-photograph:before{content:"\F09E"}.van-icon-play-circle-o:before{content:"\F09F"}.van-icon-play-circle:before{content:"\F0A0"}.van-icon-play:before{content:"\F0A1"}.van-icon-plus:before{content:"\F0A2"}.van-icon-point-gift-o:before{content:"\F0A3"}.van-icon-point-gift:before{content:"\F0A4"}.van-icon-points:before{content:"\F0A5"}.van-icon-printer:before{content:"\F0A6"}.van-icon-qr-invalid:before{content:"\F0A7"}.van-icon-qr:before{content:"\F0A8"}.van-icon-question-o:before{content:"\F0A9"}.van-icon-question:before{content:"\F0AA"}.van-icon-records:before{content:"\F0AB"}.van-icon-refund-o:before{content:"\F0AC"}.van-icon-replay:before{content:"\F0AD"}.van-icon-scan:before{content:"\F0AE"}.van-icon-search:before{content:"\F0AF"}.van-icon-send-gift-o:before{content:"\F0B0"}.van-icon-send-gift:before{content:"\F0B1"}.van-icon-service-o:before{content:"\F0B2"}.van-icon-service:before{content:"\F0B3"}.van-icon-setting-o:before{content:"\F0B4"}.van-icon-setting:before{content:"\F0B5"}.van-icon-share:before{content:"\F0B6"}.van-icon-shop-collect-o:before{content:"\F0B7"}.van-icon-shop-collect:before{content:"\F0B8"}.van-icon-shop-o:before{content:"\F0B9"}.van-icon-shop:before{content:"\F0BA"}.van-icon-shopping-cart-o:before{content:"\F0BB"}.van-icon-shopping-cart:before{content:"\F0BC"}.van-icon-shrink:before{content:"\F0BD"}.van-icon-sign:before{content:"\F0BE"}.van-icon-smile-comment-o:before{content:"\F0BF"}.van-icon-smile-comment:before{content:"\F0C0"}.van-icon-smile-o:before{content:"\F0C1"}.van-icon-smile:before{content:"\F0C2"}.van-icon-star-o:before{content:"\F0C3"}.van-icon-star:before{content:"\F0C4"}.van-icon-stop-circle-o:before{content:"\F0C5"}.van-icon-stop-circle:before{content:"\F0C6"}.van-icon-stop:before{content:"\F0C7"}.van-icon-success:before{content:"\F0C8"}.van-icon-thumb-circle-o:before{content:"\F0C9"}.van-icon-thumb-circle:before{content:"\F0CA"}.van-icon-todo-list-o:before{content:"\F0CB"}.van-icon-todo-list:before{content:"\F0CC"}.van-icon-tosend:before{content:"\F0CD"}.van-icon-tv-o:before{content:"\F0CE"}.van-icon-umbrella-circle:before{content:"\F0CF"}.van-icon-underway-o:before{content:"\F0D0"}.van-icon-underway:before{content:"\F0D1"}.van-icon-upgrade:before{content:"\F0D2"}.van-icon-user-circle-o:before{content:"\F0D3"}.van-icon-user-o:before{content:"\F0D4"}.van-icon-video-o:before{content:"\F0D5"}.van-icon-video:before{content:"\F0D6"}.van-icon-vip-card-o:before{content:"\F0D7"}.van-icon-vip-card:before{content:"\F0D8"}.van-icon-volume-o:before{content:"\F0D9"}.van-icon-volume:before{content:"\F0DA"}.van-icon-wap-home-o:before{content:"\F0DB"}.van-icon-wap-home:before{content:"\F0DC"}.van-icon-wap-nav:before{content:"\F0DD"}.van-icon-warn-o:before{content:"\F0DE"}.van-icon-warning-o:before{content:"\F0DF"}.van-icon-warning:before{content:"\F0E0"}.van-icon-weapp-nav:before{content:"\F0E1"}.van-icon-wechat:before{content:"\F0E2"}.van-icon-youzan-shield:before{content:"\F0E3"}:host{display:-webkit-inline-flex;display:inline-flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center}.van-icon--image{width:1em;height:1em}.van-icon__image{width:100%;height:100%}.van-icon__info{z-index:1} \ No newline at end of file diff --git a/litemall-wx/pages/checkout/checkout.js b/litemall-wx/pages/checkout/checkout.js index 1fe21e419..fb09d7e7b 100644 --- a/litemall-wx/pages/checkout/checkout.js +++ b/litemall-wx/pages/checkout/checkout.js @@ -162,43 +162,24 @@ Page({ }); return } - util.request(api.OrderPrepay, { + util.request(api.OrderMockPay, { orderId: orderId }, 'POST').then(function(res) { if (res.errno === 0) { - const payParam = res.data; - console.log("支付过程开始"); - wx.requestPayment({ - 'timeStamp': payParam.timeStamp, - 'nonceStr': payParam.nonceStr, - 'package': payParam.packageValue, - 'signType': payParam.signType, - 'paySign': payParam.paySign, - 'success': function(res) { - console.log("支付过程成功"); - if (grouponLinkId) { - setTimeout(() => { - wx.redirectTo({ - url: '/pages/groupon/grouponDetail/grouponDetail?id=' + grouponLinkId - }) - }, 1000); - } else { - wx.redirectTo({ - url: '/pages/payResult/payResult?status=1&orderId=' + orderId - }); - } - }, - 'fail': function(res) { - console.log("支付过程失败"); + console.log("模拟支付成功"); + if (grouponLinkId) { + setTimeout(() => { wx.redirectTo({ - url: '/pages/payResult/payResult?status=0&orderId=' + orderId - }); - }, - 'complete': function(res) { - console.log("支付过程结束") - } - }); + url: '/pages/groupon/grouponDetail/grouponDetail?id=' + grouponLinkId + }) + }, 1000); + } else { + wx.redirectTo({ + url: '/pages/payResult/payResult?status=1&orderId=' + orderId + }); + } } else { + util.showErrorToast(res.errmsg || '支付失败'); wx.redirectTo({ url: '/pages/payResult/payResult?status=0&orderId=' + orderId }); diff --git a/litemall-wx/pages/payResult/payResult.js b/litemall-wx/pages/payResult/payResult.js index 9ced6f794..c4127d848 100644 --- a/litemall-wx/pages/payResult/payResult.js +++ b/litemall-wx/pages/payResult/payResult.js @@ -31,32 +31,16 @@ Page({ }, payOrder() { let that = this; - util.request(api.OrderPrepay, { + util.request(api.OrderMockPay, { orderId: that.data.orderId }, 'POST').then(function(res) { if (res.errno === 0) { - const payParam = res.data; - console.log("支付过程开始") - wx.requestPayment({ - 'timeStamp': payParam.timeStamp, - 'nonceStr': payParam.nonceStr, - 'package': payParam.packageValue, - 'signType': payParam.signType, - 'paySign': payParam.paySign, - 'success': function(res) { - console.log("支付过程成功") - that.setData({ - status: true - }); - }, - 'fail': function(res) { - console.log("支付过程失败") - util.showErrorToast('支付失败'); - }, - 'complete': function(res) { - console.log("支付过程结束") - } + console.log("模拟支付成功") + that.setData({ + status: true }); + } else { + util.showErrorToast(res.errmsg || '支付失败'); } }); } diff --git a/litemall-wx/project.config.json b/litemall-wx/project.config.json index 0a6c5e06a..d04de34fc 100644 --- a/litemall-wx/project.config.json +++ b/litemall-wx/project.config.json @@ -32,11 +32,17 @@ "minifyWXML": true, "showES6CompileOption": false, "useCompilerPlugins": false, - "ignoreUploadUnusedFiles": true + "ignoreUploadUnusedFiles": true, + "compileWorklet": false, + "enhance": false, + "localPlugins": false, + "condition": false, + "swc": false, + "disableSWC": true }, "compileType": "miniprogram", "libVersion": "2.4.0", - "appid": "wxa5b486c6b918ecfb", + "appid": "YOUR_APPID_HEME", "projectname": "litemall-wx", "simulatorType": "wechat", "simulatorPluginLibVersion": {}, @@ -323,5 +329,10 @@ } ] } - } + }, + "packOptions": { + "ignore": [], + "include": [] + }, + "editorSetting": {} } \ No newline at end of file diff --git a/litemall-wx/project.private.config.json b/litemall-wx/project.private.config.json index 4a98289a6..e44893108 100644 --- a/litemall-wx/project.private.config.json +++ b/litemall-wx/project.private.config.json @@ -1,282 +1,24 @@ { - "setting": {}, - "condition": { - "plugin": { - "list": [] - }, - "game": { - "list": [] - }, - "gamePlugin": { - "list": [] - }, - "miniprogram": { - "list": [ - { - "id": -1, - "name": "首页", - "pathName": "pages/index/index", - "query": "" - }, - { - "id": -1, - "name": "专题", - "pathName": "pages/topic/topic", - "query": "" - }, - { - "id": -1, - "name": "专题详情", - "pathName": "pages/topicDetail/topicDetail", - "query": "id=314" - }, - { - "id": -1, - "name": "专题评论列表", - "pathName": "pages/topicComment/topicComment", - "query": "valueId=314&type=1" - }, - { - "id": -1, - "name": "专题评论添加", - "pathName": "pages/topicCommentPost/topicCommentPost", - "query": "valueId=314&type=1" - }, - { - "id": -1, - "name": "品牌", - "pathName": "pages/brand/brand", - "query": "" - }, - { - "id": -1, - "name": "品牌详情", - "pathName": "pages/brandDetail/brandDetail", - "query": "id=1001000" - }, - { - "id": -1, - "name": "人气推荐", - "pathName": "pages/hotGoods/hotGoods", - "query": "" - }, - { - "id": -1, - "name": "新品首发", - "pathName": "pages/newGoods/newGoods", - "query": "" - }, - { - "id": -1, - "name": "分类", - "pathName": "pages/catalog/catalog", - "query": "" - }, - { - "id": -1, - "name": "分类详情", - "pathName": "pages/category/category", - "query": "id=1008002" - }, - { - "id": -1, - "name": "查找", - "pathName": "pages/search/search", - "query": "" - }, - { - "name": "商品", - "pathName": "pages/goods/goods", - "query": "id=1181267", - "scene": null, - "launchMode": "default" - }, - { - "id": -1, - "name": "商品评论列表", - "pathName": "pages/comment/comment", - "query": "valueId=1181000&type=0" - }, - { - "id": -1, - "name": "购物车", - "pathName": "pages/cart/cart", - "query": "" - }, - { - "id": -1, - "name": "下单", - "pathName": "pages/checkout/checkout", - "query": "" - }, - { - "id": -1, - "name": "支付结果", - "pathName": "pages/payResult/payResult", - "query": "" - }, - { - "id": -1, - "name": "我的", - "pathName": "pages/ucenter/index/index", - "query": "" - }, - { - "id": -1, - "name": "我的订单列表", - "pathName": "pages/ucenter/order/order", - "query": "" - }, - { - "id": -1, - "name": "我的订单详情", - "pathName": "pages/ucenter/orderDetail/orderDetail", - "query": "id=1" - }, - { - "id": 22, - "name": "待评价的订单详情", - "pathName": "pages/ucenter/orderDetail/orderDetail", - "query": "id=1" - }, - { - "id": -1, - "name": "购买商品评价", - "pathName": "pages/commentPost/commentPost", - "query": "orderId=2&type=0&valueId=1116011" - }, - { - "id": 22, - "name": "我的优惠券", - "pathName": "pages/ucenter/couponList/couponList", - "query": "" - }, - { - "id": -1, - "name": "我的收藏", - "pathName": "pages/ucenter/collect/collect", - "query": "" - }, - { - "id": -1, - "name": "我的足迹", - "pathName": "pages/ucenter/footprint/footprint", - "query": "" - }, - { - "id": -1, - "name": "我的地址", - "pathName": "pages/ucenter/address/address", - "query": "" - }, - { - "id": -1, - "name": "我的地址添加", - "pathName": "pages/ucenter/addressAdd/addressAdd", - "query": "" - }, - { - "id": -1, - "name": "登录", - "pathName": "pages/auth/login/login", - "query": "" - }, - { - "id": -1, - "name": "账号登录", - "pathName": "pages/auth/accountLogin/accountLogin", - "query": "" - }, - { - "id": -1, - "name": "注册", - "pathName": "pages/auth/register/register", - "query": "" - }, - { - "id": -1, - "name": "找回密码", - "pathName": "pages/auth/reset/reset", - "query": "" - }, - { - "id": -1, - "name": "关于", - "pathName": "pages/about/about", - "query": "" - }, - { - "id": -1, - "name": "测试更新", - "pathName": "pages/index/index", - "query": "" - }, - { - "id": -1, - "name": "意见反馈", - "pathName": "pages/ucenter/feedback/feedback", - "query": "" - }, - { - "id": -1, - "name": "团购专区", - "pathName": "pages/groupon/grouponList/grouponList", - "query": "" - }, - { - "id": -1, - "name": "选择优惠券", - "pathName": "pages/ucenter/couponSelect/couponSelect", - "query": "" - }, - { - "id": -1, - "name": "我的优惠券列表", - "pathName": "pages/ucenter/couponList/couponList", - "query": "" - }, - { - "id": -1, - "name": "优惠券列表", - "pathName": "pages/coupon/coupon", - "query": "" - }, - { - "id": -1, - "name": "帮助中心", - "pathName": "pages/help/help", - "query": "", - "scene": null - }, - { - "id": -1, - "name": "我的团购", - "pathName": "pages/groupon/myGroupon/myGroupon", - "query": "", - "scene": null - }, - { - "id": -1, - "name": "申请售后", - "pathName": "pages/ucenter/aftersale/aftersale", - "query": "id=2", - "scene": null - }, - { - "id": -1, - "name": "售后列表", - "pathName": "pages/ucenter/aftersaleList/aftersaleList", - "query": "", - "scene": null - }, - { - "id": -1, - "name": "售后详情", - "pathName": "pages/ucenter/aftersaleDetail/aftersaleDetail", - "query": "id=1", - "scene": null - } - ] - } - } + "setting": { + "urlCheck": false, + "coverView": true, + "lazyloadPlaceholderEnable": false, + "skylineRenderEnable": false, + "preloadBackgroundData": false, + "autoAudits": false, + "useApiHook": true, + "useApiHostProcess": true, + "showShadowRootInWxmlPanel": true, + "useStaticServer": false, + "useLanDebug": false, + "showES6CompileOption": false, + "compileHotReLoad": true, + "checkInvalidKey": true, + "ignoreDevUnusedFiles": true, + "bigPackageSizeSupport": false, + "useIsolateContext": true + }, + "condition": {}, + "libVersion": "3.17.4", + "projectname": "litemall-wx" } \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 000000000..f61e10c8a --- /dev/null +++ b/start.bat @@ -0,0 +1,94 @@ +@echo off +setlocal enabledelayedexpansion +title litemall һ¼üÆô¶¯ +cd /d "%~dp0" + +echo ================================================ +echo litemall É̳Ç һ¼üÆô¶¯ +echo ================================================ +echo. + +REM ---- 1. ¼ì²é MySQL ·þÎñ ---- +sc query MySQL80 | find "RUNNING" >nul 2>&1 +if errorlevel 1 ( + echo [1/5] MySQL80 δÔËÐУ¬ÕýÔÚÆô¶¯... + net start MySQL80 >nul 2>&1 + if errorlevel 1 ( + echo [´íÎó] MySQL Æô¶¯ʧ°ܣ¬ÇëÊֶ¯Æô¶¯ºóÖØÊԡ£ + goto :end + ) +) +echo [1/5] MySQL80 ÔËÐÐÖÐ OK + +REM ---- 2. ¼ì²éºó¶Ë jar£¬ȱʧÔ򹹽¨ ---- +if not exist "litemall-all\target\litemall-all-0.1.0-exec.jar" ( + echo [2/5] δÕҵ½ºó¶Ë jar£¬¿ªʼ¹¹½¨£¨mvn clean package£©... + call mvn clean package -Dmaven.test.skip=true + if errorlevel 1 ( + echo [´íÎó] ºó¶˹¹½¨ʧ°ܣ¬Çë¼ì²é Maven/ÍøÂ硣 + goto :end + ) +) +echo [2/5] ºó¶Ë jar ¾ÍÐ÷ OK + +REM ---- 3. Æô¶¯ºó¶Ë 8080 ---- +netstat -ano | findstr ":8080" | findstr "LISTENING" >nul 2>&1 +if errorlevel 1 ( + echo [3/5] Æô¶¯ºó¶Ë 8080 ... + start "litemall-backend-8080" cmd /k "cd /d %~dp0 && java -Dfile.encoding=UTF-8 -jar litemall-all\target\litemall-all-0.1.0-exec.jar" +) else ( + echo [3/5] ºó¶Ë 8080 ÒÑÔÚÔËÐÐ +) + +REM ---- 4. Æô¶¯¹ÜÀíºǫ́ 9527 ---- +netstat -ano | findstr ":9527" | findstr "LISTENING" >nul 2>&1 +if errorlevel 1 ( + echo [4/5] Æô¶¯¹ÜÀíºǫ́ 9527 ... + start "litemall-admin-9527" cmd /k "cd /d %~dp0litemall-admin && set NODE_OPTIONS=--openssl-legacy-provider&& npm run dev" +) else ( + echo [4/5] ¹ÜÀíºǫ́ 9527 ÒÑÔÚÔËÐÐ +) + +REM ---- 5. Æô¶¯ÇáÉ̳Ç 6255 ---- +netstat -ano | findstr ":6255" | findstr "LISTENING" >nul 2>&1 +if errorlevel 1 ( + echo [5/5] Æô¶¯ÇáÉ̳Ç 6255 ... + start "litemall-vue-6255" cmd /k "cd /d %~dp0litemall-vue && set NODE_OPTIONS=--openssl-legacy-provider&& npm run dev" +) else ( + echo [5/5] ÇáÉ̳Ç 6255 ÒÑÔÚÔËÐÐ +) + +echo. +echo ÕýÔڵȴý·þÎñ¾ÍÐ÷£¨ǰ¶ËÊ״αàÒëԼÐè 30~60 Ã룬ÇëÎð¹رյ¯³öµĴ°¿ڣ©... +echo 8080=ºó¶Ë 9527=¹ÜÀíºǫ́ 6255=ÇáÉ̳Ç +echo. + +call :waitport 8080 60 +call :waitport 9527 90 + +echo. +echo ¹ÜÀíºǫ́ÒѾÍÐ÷£¬ÕýÔڴò¿ªä¯ÀÀÆ÷ http://localhost:9527 +start http://localhost:9527 +echo ÇáÉ̳ǵØַ£ºhttp://localhost:6255 + +goto :end + +:waitport +REM ²ÎÊý£º%1=¶˿Ú %2=×î´óµȴýÃëÊý +set /a _n=0 +:waitloop +timeout /t 3 /nobreak >nul +netstat -ano | findstr ":%1" | findstr "LISTENING" >nul 2>&1 +if not errorlevel 1 goto :waitok +set /a _n+=3 +if !_n! lss %2 goto :waitloop +echo [Ìáʾ] ¶˿Ú %1 ÔÚ %2 ÃëÄÚδ¾ÍÐ÷£¬ÇëÉԺóÊֶ¯ˢÐÂҳÃ档 +goto :eof +:waitok +echo [OK] ¶˿Ú %1 ÒѾÍÐ÷ +goto :eof + +:end +echo. +echo Íê³ɡ£¸÷·þÎñÔËÐÐÔڶÀÁ¢´°¿ÚÖУ¬¹رնÔӦ´°¿ڼ´ֹͣ¸÷þÎñ¡£ +pause >nul diff --git a/stop.bat b/stop.bat new file mode 100644 index 000000000..e0bd13403 --- /dev/null +++ b/stop.bat @@ -0,0 +1,15 @@ +@echo off +setlocal +title litemall ֹͣ·þÎñ +echo ÕýÔÚֹͣ litemall ·þÎ񣨶˿Ú 8080 / 9527 / 6255£©... + +for %%p in (8080 9527 6255) do ( + for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%%p" ^| findstr "LISTENING"') do ( + echo ֹͣ¶˿Ú %%p (PID %%a) + taskkill /f /pid %%a >nul 2>&1 + ) +) + +echo. +echo Íê³ɡ££¨MySQL ·þÎñδֹͣ£¬ÈçÐèֹͣÇëÊֶ¯ִÐÐ net stop MySQL80£© +pause >nul