QAs = QA.getQAs(getQAListPort);
+ return QAs;
+ }
+}
diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/service/UpdateQAService.java b/qa-service/qa-service-application/src/main/java/com/example/application/service/UpdateQAService.java
new file mode 100644
index 0000000..d908f68
--- /dev/null
+++ b/qa-service/qa-service-application/src/main/java/com/example/application/service/UpdateQAService.java
@@ -0,0 +1,26 @@
+package com.example.application.service;
+
+import com.example.application.command.UpdateQACommand;
+import com.example.application.port.in.UpdateQAUseCase;
+import com.example.qa.service.domain.QA;
+import com.example.qa.service.domain.port.UpdateQAPort;
+import com.example.qa.service.domain.valueobject.*;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+/**
+ * @author lizishan
+ */
+@Service
+public class UpdateQAService implements UpdateQAUseCase {
+ @Resource
+ private UpdateQAPort updateQAPort;
+
+ @Override
+ public QA updateQA(UpdateQACommand command) {
+ QA QA = new QA(
+ new QAId(command.id()),
+ new Question(command.question()),
+ new Answer(command.answer()));
+ return updateQAPort.updateQA(QA);
+ }
+}
diff --git a/qa-service/qa-service-bootstrap/src/main/resources/application.properties b/qa-service/qa-service-bootstrap/src/main/resources/application.properties
new file mode 100644
index 0000000..e073491
--- /dev/null
+++ b/qa-service/qa-service-bootstrap/src/main/resources/application.properties
@@ -0,0 +1,27 @@
+server.port=28080
+
+spring.application.name=user-service
+
+
+# Nacos认证信息
+spring.cloud.nacos.discovery.username=nacos
+spring.cloud.nacos.discovery.password=nacos
+# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
+spring.cloud.nacos.discovery.server-addr=192.168.168.128:8848
+# 注册到 nacos 的指定 namespace,默认为 public
+spring.cloud.nacos.discovery.namespace=public
+
+# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
+# Nacos认证信息
+spring.cloud.nacos.config.username=nacos
+spring.cloud.nacos.config.password=nacos
+spring.cloud.nacos.config.contextPath=/nacos
+# 设置配置中心服务端地址
+spring.cloud.nacos.config.server-addr=192.168.168.128::8848
+# Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,请不要填写这个值,直接留空即可
+# spring.cloud.nacos.config.namespace=
+spring.config.import=nacos:${spring.application.name}.properties?refresh=true
+
+
+
+
diff --git a/qa-service/qa-service-common/src/main/java/com/example/qa/service/common/IdWorker.java b/qa-service/qa-service-common/src/main/java/com/example/qa/service/common/IdWorker.java
new file mode 100644
index 0000000..ac96e23
--- /dev/null
+++ b/qa-service/qa-service-common/src/main/java/com/example/qa/service/common/IdWorker.java
@@ -0,0 +1,199 @@
+package com.example.qa.service.common;
+
+import java.lang.management.ManagementFactory;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+
+/**
+ * @author wuyunbin
+ * 名称:IdWorker.java
+ * 描述:分布式自增长ID
+ *
+ * Twitter的 Snowflake JAVA实现方案
+ *
+ * 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
+ * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
+ * 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
+ * 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
+ * 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
+ * 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
+ * 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
+ *
+ * 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
+ * @author Polim
+ */
+public class IdWorker {
+ /**
+ * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
+ */
+ private final static long TWEPOCH = 1288834974657L;
+
+ /**
+ * 机器标识位数
+ */
+ private final static long WORKER_ID_BITS = 5L;
+
+ /**
+ * 数据中心标识位数
+ */
+ private final static long DATA_CENTER_ID_BITS = 5L;
+
+ /**
+ * 机器ID最大值
+ */
+ private final static long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
+
+ /**
+ * 数据中心ID最大值
+ */
+ private final static long MAX_DATACENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
+
+ /**
+ * 毫秒内自增位
+ */
+ private final static long SEQUENCE_BITS = 12L;
+
+ /**
+ * 机器ID偏左移12位
+ */
+ private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
+
+ /**
+ * 数据中心ID左移17位
+ */
+ private final static long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
+
+ /**
+ * 时间毫秒左移22位
+ */
+ private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
+
+ private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
+
+ /**
+ * 上次生产id时间戳
+ */
+ private static long lastTimestamp = -1L;
+
+ /**
+ * 0,并发控制
+ */
+ private long sequence = 0L;
+
+ private final long workerId;
+
+ /**
+ * 数据标识id部分
+ */
+ private final long datacenterId;
+
+ public IdWorker() {
+ this.datacenterId = getDatacenterId();
+ this.workerId = getMaxWorkerId(datacenterId);
+ }
+
+ /**
+ * @param workerId 工作机器ID
+ * @param datacenterId 序列号
+ */
+ public IdWorker(long workerId, long datacenterId) {
+ if (workerId > MAX_WORKER_ID || workerId < 0) {
+ throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
+ }
+ if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
+ throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATACENTER_ID));
+ }
+ this.workerId = workerId;
+ this.datacenterId = datacenterId;
+ }
+
+ /**
+ * 获取下一个ID
+ *
+ * @return
+ */
+ public synchronized long nextId() {
+ long timestamp = timeGen();
+ if (timestamp < lastTimestamp) {
+ throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
+ }
+
+ if (lastTimestamp == timestamp) {
+ // 当前毫秒内,则+1
+ sequence = (sequence + 1) & SEQUENCE_MASK;
+ if (sequence == 0) {
+ // 当前毫秒内计数满了,则等待下一秒
+ timestamp = tilNextMillis(lastTimestamp);
+ }
+ } else {
+ sequence = 0L;
+ }
+ lastTimestamp = timestamp;
+ // ID偏移组合生成最终的ID,并返回ID
+
+ return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
+ | (datacenterId << DATACENTER_ID_SHIFT)
+ | (workerId << WORKER_ID_SHIFT) | sequence;
+ }
+
+ private long tilNextMillis(final long lastTimestamp) {
+ long timestamp = this.timeGen();
+ while (timestamp <= lastTimestamp) {
+ timestamp = this.timeGen();
+ }
+ return timestamp;
+ }
+
+ private long timeGen() {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ *
+ * 获取 MAX_WORKER_ID
+ *
+ */
+ protected static long getMaxWorkerId(long datacenterId) {
+ StringBuilder mpid = new StringBuilder();
+ mpid.append(datacenterId);
+ String name = ManagementFactory.getRuntimeMXBean().getName();
+ if (!name.isEmpty()) {
+ /*
+ * GET jvmPid
+ */
+ mpid.append(name.split("@")[0]);
+ }
+ /*
+ * MAC + PID 的 hashcode 获取16个低位
+ */
+ return (mpid.toString().hashCode() & 0xffff) % (IdWorker.MAX_WORKER_ID + 1);
+ }
+
+ /**
+ *
+ * 数据标识id部分
+ *
+ */
+ protected static long getDatacenterId() {
+ long id = 0L;
+ try {
+ InetAddress ip = InetAddress.getLocalHost();
+ NetworkInterface network = NetworkInterface.getByInetAddress(ip);
+ if (network == null) {
+ id = 1L;
+ } else {
+ byte[] mac = network.getHardwareAddress();
+ id = ((0x000000FF & (long) mac[mac.length - 1])
+ | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
+ id = id % (IdWorker.MAX_DATACENTER_ID + 1);
+ }
+ } catch (Exception e) {
+ System.out.println(" getDatacenterId: " + e.getMessage());
+ }
+ return id;
+ }
+
+
+
+
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/QA.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/QA.java
new file mode 100644
index 0000000..c40b3bb
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/QA.java
@@ -0,0 +1,48 @@
+package com.example.qa.service.domain;
+
+import com.example.qa.service.common.IdWorker;
+import com.example.qa.service.domain.port.GetQAListPort;
+import com.example.qa.service.domain.valueobject.Answer;
+import com.example.qa.service.domain.valueobject.QAId;
+import com.example.qa.service.domain.valueobject.Question;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.List;
+
+@Setter
+@Getter
+@ToString
+/**
+ * @author yangwenkai
+ * QA类
+ */
+public class QA {
+ private QAId id;
+ private Question question;
+ private Answer answer;
+
+ public QA() {
+ }
+
+ public QA(QAId id, Question question, Answer answer) {
+ this.id = id;
+ this.question = question;
+ this.answer = answer;
+ }
+
+ public QA(Question question, Answer answer) {
+ this.id= genId();
+ this.question = question;
+ this.answer = answer;
+ }
+
+ public static List getQAs(GetQAListPort getQAListPort){
+ return getQAListPort.getQAs();
+ }
+
+ public QAId genId(){
+ return new QAId(new IdWorker().nextId());
+ }
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/CreateQAPort.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/CreateQAPort.java
new file mode 100644
index 0000000..aeb293a
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/CreateQAPort.java
@@ -0,0 +1,11 @@
+package com.example.qa.service.domain.port;
+
+import com.example.qa.service.domain.QA;
+
+/**
+ * @author yangwenkai
+ * 创建QA的端口
+ */
+public interface CreateQAPort {
+ QA createQA(QA qa);
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/DeleteQAPort.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/DeleteQAPort.java
new file mode 100644
index 0000000..2ea8e47
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/DeleteQAPort.java
@@ -0,0 +1,8 @@
+package com.example.qa.service.domain.port;
+
+/**
+ * @author qiuyujie
+ */
+public interface DeleteQAPort {
+ void deleteQA(Long id);
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/GetQAByIdPort.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/GetQAByIdPort.java
new file mode 100644
index 0000000..fdc3b92
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/GetQAByIdPort.java
@@ -0,0 +1,10 @@
+package com.example.qa.service.domain.port;
+
+import com.example.qa.service.domain.QA;
+
+/**
+ * @author dengli
+ */
+public interface GetQAByIdPort {
+ QA getQAById(long id);
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/GetQAListPort.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/GetQAListPort.java
new file mode 100644
index 0000000..3fc891d
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/GetQAListPort.java
@@ -0,0 +1,11 @@
+package com.example.qa.service.domain.port;
+
+import com.example.qa.service.domain.QA;
+
+import java.util.List;
+/**
+ * @author wangqingqing
+ */
+public interface GetQAListPort {
+ List getQAs();
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/UpdateQAPort.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/UpdateQAPort.java
new file mode 100644
index 0000000..5713b79
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/port/UpdateQAPort.java
@@ -0,0 +1,9 @@
+package com.example.qa.service.domain.port;
+
+import com.example.qa.service.domain.QA;
+/**
+ * @author lizishan
+ */
+public interface UpdateQAPort {
+ QA updateQA(QA qa);
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/Answer.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/Answer.java
new file mode 100644
index 0000000..8a34b3c
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/Answer.java
@@ -0,0 +1,10 @@
+package com.example.qa.service.domain.valueobject;
+
+/**
+ * @author qiuyujie
+ */
+public record Answer(String answer) {
+ public String getValue() {
+ return answer;
+ }
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/QAId.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/QAId.java
new file mode 100644
index 0000000..a423600
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/QAId.java
@@ -0,0 +1,10 @@
+package com.example.qa.service.domain.valueobject;
+
+/**
+ * @author dengli
+ */
+public record QAId(long id) {
+ public long getValue() {
+ return id;
+ }
+}
diff --git a/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/Question.java b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/Question.java
new file mode 100644
index 0000000..773a8f9
--- /dev/null
+++ b/qa-service/qa-service-domain/src/main/java/com/example/qa/service/domain/valueobject/Question.java
@@ -0,0 +1,9 @@
+package com.example.qa.service.domain.valueobject;
+/**
+ * @author wangqingqing
+ */
+public record Question(String question) {
+ public String getValue() {
+ return question;
+ }
+}
--
Gitee
From 1d805cee0913e3c0b73de462ba03a9ebecc02f84 Mon Sep 17 00:00:00 2001
From: Xiaya_kevin <1415130181@qq.com>
Date: Wed, 10 Sep 2025 17:36:41 +0800
Subject: [PATCH 2/5] =?UTF-8?q?fix(qa-service-bootstrap):=20AI=E8=AF=B4?=
=?UTF-8?q?=E8=BF=99=E4=B8=AA=E5=8F=8C=E5=86=92=E5=8F=B7=E8=A6=81=E5=8E=BB?=
=?UTF-8?q?=E6=8E=89=E4=B8=80=E4=B8=AA=E5=86=92=E5=8F=B7=EF=BC=9B=E6=8A=8A?=
=?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=BC=8F=E6=8E=89=E7=9A=84user=E6=94=B9?=
=?UTF-8?q?=E5=9B=9E=E6=9D=A5=E4=BA=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/compiler.xml | 8 ------
.idea/encodings.xml | 4 ---
.idea/gradle.xml | 6 -----
.idea/misc.xml | 6 -----
qa-service/pom.xml | 2 +-
.../src/main/resources/application.properties | 27 -------------------
6 files changed, 1 insertion(+), 52 deletions(-)
delete mode 100644 .idea/compiler.xml
delete mode 100644 .idea/encodings.xml
delete mode 100644 .idea/gradle.xml
delete mode 100644 .idea/misc.xml
delete mode 100644 qa-service/qa-service-bootstrap/src/main/resources/application.properties
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index a1757ae..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index da0415a..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index c4be5fb..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 7280f00..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/qa-service/pom.xml b/qa-service/pom.xml
index 28b1393..7ba8bd5 100644
--- a/qa-service/pom.xml
+++ b/qa-service/pom.xml
@@ -79,7 +79,7 @@
spring-boot-maven-plugin
${spring-boot.version}
- com.example.user-service.user-serviceApplication
+ com.example.qa-service.qa-serviceApplication
true
diff --git a/qa-service/qa-service-bootstrap/src/main/resources/application.properties b/qa-service/qa-service-bootstrap/src/main/resources/application.properties
deleted file mode 100644
index e073491..0000000
--- a/qa-service/qa-service-bootstrap/src/main/resources/application.properties
+++ /dev/null
@@ -1,27 +0,0 @@
-server.port=28080
-
-spring.application.name=user-service
-
-
-# Nacos认证信息
-spring.cloud.nacos.discovery.username=nacos
-spring.cloud.nacos.discovery.password=nacos
-# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
-spring.cloud.nacos.discovery.server-addr=192.168.168.128:8848
-# 注册到 nacos 的指定 namespace,默认为 public
-spring.cloud.nacos.discovery.namespace=public
-
-# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
-# Nacos认证信息
-spring.cloud.nacos.config.username=nacos
-spring.cloud.nacos.config.password=nacos
-spring.cloud.nacos.config.contextPath=/nacos
-# 设置配置中心服务端地址
-spring.cloud.nacos.config.server-addr=192.168.168.128::8848
-# Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,请不要填写这个值,直接留空即可
-# spring.cloud.nacos.config.namespace=
-spring.config.import=nacos:${spring.application.name}.properties?refresh=true
-
-
-
-
--
Gitee
From 5b0f51a02dccd55b6ec78a2d95a353b184cb4527 Mon Sep 17 00:00:00 2001
From: Xiaya_kevin <1415130181@qq.com>
Date: Thu, 11 Sep 2025 11:22:55 +0800
Subject: [PATCH 3/5] =?UTF-8?q?feat(user):=20AI=E7=9A=843=E7=A7=8D?=
=?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=B3=A8=E5=85=A5=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/example/user/service/domain/User.java | 47 ++++++++++++++++++-
1 file changed, 45 insertions(+), 2 deletions(-)
diff --git a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
index e5e3e37..9e8aebc 100644
--- a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
+++ b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
@@ -21,10 +21,25 @@ public class User {
private Email email;
//todo 需要修改会Password类型
private String password;
+
+ // 字段注入方式 - 通过@Resource或@Autowired注解注入
+ private GetUserByNamePort getUserByNamePort;
+ private GetUserListPort getUserListPort;
public User() {
}
+ // 构造函数注入方式
+ public User(UserId id, UserName name, UserAge age, Email email,
+ GetUserByNamePort getUserByNamePort, GetUserListPort getUserListPort) {
+ this.id = id;
+ this.name = name;
+ this.age = age;
+ this.email = email;
+ this.getUserByNamePort = getUserByNamePort;
+ this.getUserListPort = getUserListPort;
+ }
+
public User(UserId id, UserName name, UserAge age, Email email) {
this.id = id;
this.name = name;
@@ -39,14 +54,42 @@ public class User {
this.email = email;
}
+ // Setter方法注入方式
+ public void setGetUserByNamePort(GetUserByNamePort getUserByNamePort) {
+ this.getUserByNamePort = getUserByNamePort;
+ }
+
+ public void setGetUserListPort(GetUserListPort getUserListPort) {
+ this.getUserListPort = getUserListPort;
+ }
+
+ // 使用字段注入的业务方法
+ public User getUserByNameWithFieldInjection(String name) {
+ if (getUserByNamePort == null) {
+ throw new IllegalStateException("GetUserByNamePort依赖未注入");
+ }
+ return getUserByNamePort.getUserByName(name);
+ }
+
+ // 使用构造函数注入的业务方法
+ public static User getUserByNameWithConstructorInjection(String name, GetUserByNamePort getUserByNamePort) {
+ return getUserByNamePort.getUserByName(name);
+ }
+
+ // 使用setter注入的业务方法
+ public User getUserByNameWithSetterInjection(String name) {
+ if (getUserByNamePort == null) {
+ throw new IllegalStateException("GetUserByNamePort依赖未注入,请先调用setGetUserByNamePort方法");
+ }
+ return getUserByNamePort.getUserByName(name);
+ }
public static List getUsers(GetUserListPort getUserListPort){
return getUserListPort.getUsers();
}
-
public static User getUserByName(String name, GetUserByNamePort getUserByNamePort){
- User user = getUserByNamePort.getUserByName(name,getUserByNamePort);
+ User user = getUserByNamePort.getUserByName(name);
return user;
}
--
Gitee
From 846bfdd0b5e45c297aa130a4e24a0370573d1b8f Mon Sep 17 00:00:00 2001
From: Xiaya_kevin <1415130181@qq.com>
Date: Thu, 11 Sep 2025 11:51:56 +0800
Subject: [PATCH 4/5] =?UTF-8?q?feat(user):=20AI=E7=9A=84=E9=9D=99=E6=80=81?=
=?UTF-8?q?=E6=B3=A8=E5=85=A5=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/example/user/service/domain/User.java | 22 +++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
index 9e8aebc..0de0058 100644
--- a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
+++ b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
@@ -1,9 +1,11 @@
package com.example.user.service.domain;
import com.example.user.service.common.IdWorker;
+import com.example.user.service.domain.port.GetUserByIdPort;
import com.example.user.service.domain.port.GetUserByNamePort;
import com.example.user.service.domain.port.GetUserListPort;
import com.example.user.service.domain.valueobject.*;
+import jakarta.annotation.Resource;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@@ -88,8 +90,24 @@ public class User {
return getUserListPort.getUsers();
}
- public static User getUserByName(String name, GetUserByNamePort getUserByNamePort){
- User user = getUserByNamePort.getUserByName(name);
+ @Resource
+ private GetUserByIdPort getUserByIdPort;
+
+ // 静态字段注入 - 用于静态方法
+ private static GetUserByNamePort staticGetUserByNamePort;
+
+ // 静态注入方法 - 由Spring容器调用
+ @Resource
+ public void setStaticGetUserByNamePort(GetUserByNamePort getUserByNamePort) {
+ User.staticGetUserByNamePort = getUserByNamePort;
+ }
+
+ // 静态方法 - 只使用name参数,通过静态字段获取依赖
+ public static User getUserByName(String name){
+ if (staticGetUserByNamePort == null) {
+ throw new IllegalStateException("GetUserByNamePort依赖未注入,请确保Spring容器已初始化");
+ }
+ User user = staticGetUserByNamePort.getUserByName(name);
return user;
}
--
Gitee
From ae2655d4c5dbbe1227c10dc990b3826f475796e1 Mon Sep 17 00:00:00 2001
From: Xiaya_kevin <1415130181@qq.com>
Date: Thu, 11 Sep 2025 17:49:40 +0800
Subject: [PATCH 5/5] =?UTF-8?q?feat(user-service=EF=BC=8Cuser-service-appl?=
=?UTF-8?q?ication=EF=BC=8Cuser-adapter-out-persistence=EF=BC=8Cuser-adapt?=
=?UTF-8?q?er-in-web):=20AI=E7=9A=84=E9=9D=99=E6=80=81=E6=B3=A8=E5=85=A5?=
=?UTF-8?q?=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
添加了密码验证功能:1、密码限制长度2、密码包括字符符号3、密码不为空
为登入创建JWT令牌
---
.../in/web/controller/UserController.java | 2 +
.../in/web/dto/CreateUserRequestDTO.java | 13 +-
.../persistence/convertor/UserConvertor.java | 6 +-
.../out/persistence/entity/UserEntity.java | 3 +-
user-service/user-service-application/pom.xml | 19 +++
.../service/CreateUserService.java | 3 +-
.../application/service/UserLoginService.java | 22 +++-
.../com/example/user/service/domain/User.java | 13 +-
.../service/domain/valueobject/Password.java | 120 ++++++++++++++++--
9 files changed, 177 insertions(+), 24 deletions(-)
diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java
index 544dd4c..453b0fc 100644
--- a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java
+++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java
@@ -50,6 +50,8 @@ public class UserController {
@PostMapping()
public User createUser(@RequestBody CreateUserRequestDTO createUserRequestDTO){
+ // 验证密码是否符合要求
+ createUserRequestDTO.validatePassword();
CreateUserCommand command=CreateUserCommand.builder()
.name(createUserRequestDTO.name())
diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java
index 9ebf615..883965d 100644
--- a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java
+++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java
@@ -1,10 +1,21 @@
package com.example.user.adapter.in.web.dto;
+import com.example.user.service.domain.valueobject.Password;
+
public record CreateUserRequestDTO(
String name,
Integer age,
String email,
String password,
String rePassword) {
- // TODO: 密码校验
+
+ /**
+ * 验证密码是否符合要求
+ * 包括密码长度、复杂度以及密码和确认密码的一致性
+ * @author yangwenkai
+ * @throws IllegalArgumentException 如果密码不符合要求
+ */
+ public void validatePassword() {
+ Password.validatePassword(password, rePassword);
+ }
}
diff --git a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/convertor/UserConvertor.java b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/convertor/UserConvertor.java
index 6f79540..258dd8c 100644
--- a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/convertor/UserConvertor.java
+++ b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/convertor/UserConvertor.java
@@ -12,7 +12,8 @@ public class UserConvertor {
new UserName(userEntity.getName()),
new UserAge(userEntity.getAge()),
new Email(userEntity.getEmail()),
- new Password(userEntity.getPassword())
+ new Password(userEntity.getPassword()),
+ userEntity.getIsSuper()
);
}
@@ -22,7 +23,8 @@ public class UserConvertor {
user.getName().getValue(),
user.getAge().getValue(),
user.getEmail().getValue(),
- user.getPassword().encryptedValue()
+ user.getPassword().encryptedValue(),
+ user.getIsSuper()
);
}
}
diff --git a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/entity/UserEntity.java b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/entity/UserEntity.java
index b64228d..7cd5705 100644
--- a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/entity/UserEntity.java
+++ b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/entity/UserEntity.java
@@ -18,7 +18,6 @@ public class UserEntity {
private Integer age;
private String email;
private String password;
+ private Boolean isSuper;
- public UserEntity(long value, String value1, int value2, String value3) {
- }
}
diff --git a/user-service/user-service-application/pom.xml b/user-service/user-service-application/pom.xml
index df05045..2b46f97 100644
--- a/user-service/user-service-application/pom.xml
+++ b/user-service/user-service-application/pom.xml
@@ -36,6 +36,25 @@
user-service-domain
0.0.1-SNAPSHOT
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.12.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.12.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.12.5
+ runtime
+
diff --git a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java
index 931828a..8e79e43 100644
--- a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java
+++ b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java
@@ -25,7 +25,8 @@ public class CreateUserService implements CreateUserUseCase {
new UserAge(createUserCommand.age()),
new Email(createUserCommand.email()),
// new Password( createUserCommand.password())
- Password.fromRaw(createUserCommand.password())
+ Password.fromRaw(createUserCommand.password()),
+ false // 默认不是超级用户
);
log.info("user:{}",user);
return createUserPort.createUser(user);
diff --git a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UserLoginService.java b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UserLoginService.java
index 4240269..041d5c2 100644
--- a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UserLoginService.java
+++ b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UserLoginService.java
@@ -2,6 +2,7 @@ package com.example.user.service.application.service;
import com.example.user.service.application.command.UserLoginCommand;
import com.example.user.service.application.port.in.UserLoginUseCase;
+import com.example.user.service.application.util.JwtUtil;
import com.example.user.service.domain.User;
import com.example.user.service.domain.port.GetUserByNamePort;
import jakarta.annotation.Resource;
@@ -14,6 +15,9 @@ public class UserLoginService implements UserLoginUseCase {
@Resource
private GetUserByNamePort getUserByNamePort;
+
+ @Resource
+ private JwtUtil jwtUtil;
@Override
public String login(UserLoginCommand userLoginCommand) {
@@ -28,10 +32,20 @@ public class UserLoginService implements UserLoginUseCase {
throw new RuntimeException("密码错误");
}
//签发token
- /*
- todo 封装一个JwtUtil实现jwt签发
- token 有效期 5min ,key=123456 ,载荷:{name:user.name,id:user.id,is_super}
+ /**
+ * @author yangwenkai
*/
- return "token";
+ // 使用JwtUtil生成JWT令牌,包含用户信息
+ // 假设普通用户is_super为false,超级用户需要根据业务逻辑确定
+ boolean isSuperUser = false; // 这里可以根据用户角色或其他条件设置
+
+ String token = jwtUtil.generateToken(
+ user.getId().getValue(), // 用户ID
+ user.getName().getValue(), // 用户名
+ isSuperUser // 是否是超级用户
+ );
+
+ log.info("用户登录成功,生成JWT令牌: {}", token);
+ return token;
}
}
diff --git a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
index 88945ed..e72a5be 100644
--- a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
+++ b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java
@@ -21,30 +21,33 @@ public class User {
private UserAge age;
private Email email;
private Password password;
+ private Boolean isSuper;
public User() {
}
- public User(UserId id, UserName name, UserAge age, Email email, Password password) {
+ public User(UserId id, UserName name, UserAge age, Email email, Password password, Boolean isSuper) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
this.password = password;
+ this.isSuper = isSuper;
}
- public User( UserName name, UserAge age, Email email, Password password) {
+ public User( UserName name, UserAge age, Email email, Password password, Boolean isSuper) {
this.id= genId() ;
this.name = name;
this.age = age;
this.email = email;
this.password = password;
+ this.isSuper = isSuper;
}
public User(UserId userId, UserName userName, UserAge userAge, Email email) {
- this.id = id;
- this.name = name;
- this.age = age;
+ this.id = userId;
+ this.name = userName;
+ this.age = userAge;
this.email = email;
}
diff --git a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/Password.java b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/Password.java
index 57d9be7..5fbaea1 100644
--- a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/Password.java
+++ b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/Password.java
@@ -4,36 +4,138 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
- *
- * @param encryptedValue 密码 明文密码还是密文密码?->密文
+ * 密码值对象
+ * 负责密码的验证、加密和存储
+ *
+ * @param encryptedValue 加密后的密码值
*/
@Slf4j
public record Password(String encryptedValue) {
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+ /**
+ * 从原始密码创建Password对象
+ * 会先验证密码复杂度,然后进行加密
+ * @author qiuyujie
+ * @param rawPassword 原始密码
+ * @return 加密后的Password对象
+ * @throws IllegalArgumentException 如果密码不符合复杂度要求
+ */
public static Password fromRaw(String rawPassword) {
- if (rawPassword == null || rawPassword.length() < 6) {
- throw new RuntimeException("密码长度至少6位");
- }
-
- log.info("密码明文: {},密码密文{}", rawPassword,encoder.encode(rawPassword));
- return new Password(encoder.encode(rawPassword));
+ validatePasswordComplexity(rawPassword);
+ String encryptedPassword = encoder.encode(rawPassword);
+ log.info("密码明文: {},密码密文{}", rawPassword, encryptedPassword);
+ return new Password(encryptedPassword);
}
/**
* 从已加密的密码创建Password对象
* 用于从持久化层恢复数据
+ * @author qiuyujie
+ * @param encryptedPassword 已加密的密码
+ * @return Password对象
*/
public static Password fromEncrypted(String encryptedPassword) {
return new Password(encryptedPassword);
}
+ /**
+ * 验证原始密码是否与加密密码匹配
+ * @author dengli
+ * @param rawPassword 原始密码
+ * @return 是否匹配
+ */
public boolean verify(String rawPassword) {
return encoder.matches(rawPassword, encryptedValue);
}
- // 用于密码重置
+ /**
+ * 用于密码重置
+ * @author dengli
+ * @param encryptedValue 加密后的密码值
+ * @return Password对象
+ */
public static Password of(String encryptedValue) {
return new Password(encryptedValue);
}
+
+ /**
+ * 验证密码长度
+ * @author wangqingqing
+ * @param password 待验证的密码
+ * @throws IllegalArgumentException 如果密码长度不符合要求
+ */
+ public static void validatePasswordLength(String password) {
+ if (password == null) {
+ throw new IllegalArgumentException("密码不能为空");
+ }
+ if (password.length() < 6) {
+ throw new IllegalArgumentException("密码长度不足,至少需要6个字符");
+ }
+ if (password.length() > 20) {
+ throw new IllegalArgumentException("密码长度过长,最多允许20个字符");
+ }
+ }
+
+ /**
+ * 验证密码复杂度
+ * @@author wangqingqing
+ * @param password 待验证的密码
+ * @throws IllegalArgumentException 根据具体缺失的元素提供详细的错误信息
+ */
+ public static void validatePasswordComplexity(String password) {
+ if (password == null) {
+ throw new IllegalArgumentException("密码不能为空");
+ }
+
+ boolean hasLetter = password.matches(".*[A-Za-z].*");
+ boolean hasDigit = password.matches(".*\\d.*");
+
+ if (!hasLetter && !hasDigit) {
+ throw new IllegalArgumentException("密码必须包含至少一个字母和一个数字");
+ }
+ if (!hasLetter) {
+ throw new IllegalArgumentException("密码必须包含至少一个字母");
+ }
+ if (!hasDigit) {
+ throw new IllegalArgumentException("密码必须包含至少一个数字");
+ }
+
+ // 额外的复杂度检查:特殊字符(可选)
+ boolean hasSpecialChar = password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/?].*");
+ if (!hasSpecialChar) {
+ // 这里只是记录,不抛出异常,因为特殊字符不是强制要求
+ // 可以根据需要调整为强制要求
+ }
+ }
+
+ /**
+ * 验证密码和确认密码是否一致
+ * @author lizishan
+ * @param password 密码
+ * @param rePassword 确认密码
+ * @throws IllegalArgumentException 根据具体情况提供详细的错误信息
+ */
+ public static void validatePasswordMatch(String password, String rePassword) {
+ if (rePassword == null) {
+ throw new IllegalArgumentException("确认密码不能为空");
+ }
+ if (!password.equals(rePassword)) {
+ throw new IllegalArgumentException("密码和确认密码不匹配,请重新输入");
+ }
+ }
+
+ /**
+ * 完整的密码验证流程
+ * 包括密码长度、复杂度以及密码和确认密码的一致性
+ * @author lizishan
+ * @param password 密码
+ * @param rePassword 确认密码
+ * @throws IllegalArgumentException 如果密码不符合要求
+ */
+ public static void validatePassword(String password, String rePassword) {
+ validatePasswordLength(password);
+ validatePasswordComplexity(password);
+ validatePasswordMatch(password, rePassword);
+ }
}
--
Gitee