diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b5d4abcede2c102d29e872aea2c572d13b76708d..0000000000000000000000000000000000000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 6faefa1b4285b2a1c85776b7e1cf2540344bdc81..0000000000000000000000000000000000000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 514d7a6906b5190d87023363f3b116c9dab4144a..0000000000000000000000000000000000000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 40fbb783660cdcfbdcce2c9f8700d668dfe1d5d0..0000000000000000000000000000000000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/qa-service/docker-compose.yml b/qa-service/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..b92c130edf1cd7cac69d42ab559368b344fc1b40 --- /dev/null +++ b/qa-service/docker-compose.yml @@ -0,0 +1,250 @@ +networks: + app_net: + driver: bridge +services: + # MySQL 主库 + mysql: + image: mysql:8.4 + container_name: mysql + environment: + - MYSQL_ROOT_PASSWORD=root + - TZ=Asia/Shanghai + - MYSQL_CHARSET=utf8mb4 + - MYSQL_COLLATION=utf8mb4_general_ci + - MYSQL_ROOT_HOST=% + - MYSQL_SSL_MODE=REQUIRED + ports: + - "3306:3306" + privileged: true + volumes: + - ./mysql/conf.d:/etc/mysql/conf.d + - ./mysql/data:/var/lib/mysql + healthcheck: + test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ] + interval: 10s + timeout: 5s + retries: 5 + networks: + - app_net + restart: always + redis: + container_name: redis + image: redis:latest + ports: + - "6379:6379" + privileged: true + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 5s + timeout: 3s + retries: 5 + networks: + - app_net + restart: always + + rabbitmq: + container_name: rabbitmq + image: rabbitmq:4.1.2-management + ports: + - "5672:5672" + - "15672:15672" + privileged: true + healthcheck: + test: [ "CMD", "rabbitmqctl", "status" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + restart: always + minio: + image: quay.io/minio/minio:latest + container_name: minio + restart: always + ports: + - "9000:9000" # S3 API 端口 + - "9001:9001" # Web 控制台端口 + environment: + MINIO_ROOT_USER: admin + MINIO_ROOT_PASSWORD: admin123456 + volumes: + - ./minio-data:/data + command: server /data --console-address ":9001" + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + # RocketMQ NameServer + rocketmq-namesrv: + image: apache/rocketmq:latest + container_name: rocketmq-namesrv + command: sh mqnamesrv + ports: + - "9876:9876" + environment: + JAVA_OPT_EXT: "-server -Xms128m -Xmx128m -Xmn128m" + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:9876" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + restart: always + + # RocketMQ Broker + rocketmq-broker: + image: apache/rocketmq:latest + container_name: rocketmq-broker + command: sh mqbroker -c /home/rocketmq/rocketmq-5.3.3/conf/broker.conf + ports: + - "10909:10909" + - "10911:10911" + - "10912:10912" + environment: + JAVA_OPT_EXT: "-server -Xms128m -Xmx128m -Xmn128m" + NAMESRV_ADDR: "rocketmq-namesrv:9876" + volumes: + - ./rocketmq/conf/broker.conf:/home/rocketmq/rocketmq-5.3.3/conf/broker.conf + - ./rocketmq/logs:/home/rocketmq/logs + - ./rocketmq/store:/home/rocketmq/store + depends_on: + - rocketmq-namesrv + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:10911" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + restart: always + + # Elasticsearch + elasticsearch: + image: elasticsearch:7.17.28 + container_name: elasticsearch + environment: + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms512m -Xmx512m + - xpack.security.enabled=false + ports: + - "9200:9200" + - "9300:9300" + volumes: + - ./es/data:/usr/share/elasticsearch/data + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:9200" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + restart: always + + # Nginx + nginx: + image: nginx:latest + container_name: nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/conf.d:/etc/nginx/conf.d + - ./nginx/html:/usr/share/nginx/html + - ./nginx/logs:/var/log/nginx + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + restart: always + nacos: + container_name: nacos + image: nacos/nacos-server:v3.0.2 + ports: + - "8848:8848" + - "8080:8080" + - "9848:9848" + - "9849:9849" + environment: + - TZ=Asia/Shanghai + - MODE=standalone + - PREFER_HOST_MODE=hostname + - SPRING_DATASOURCE_PLATFORM=mysql + - MYSQL_SERVICE_HOST=mysql + - MYSQL_SERVICE_DB_NAME=nacos_config + - MYSQL_SERVICE_PORT=3306 + - MYSQL_SERVICE_USER=root + - MYSQL_SERVICE_PASSWORD=root + - MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + - NACOS_AUTH_IDENTITY_KEY=2222 + - NACOS_AUTH_IDENTITY_VALUE=2xxx + - NACOS_AUTH_TOKEN=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg= + volumes: + - ./nacos/logs:/home/nacos/logs + depends_on: + mysql: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8848/nacos" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + privileged: true + restart: always + # Seata + seata-server: + image: seataio/seata-server:1.6.0 + container_name: seata-server + ports: + - "7091:7091" + - "8091:8091" + networks: + - app_net + restart: always + + # Sentinel + sentinel: + image: bladex/sentinel-dashboard:latest + container_name: sentinel + ports: + - "8858:8858" + environment: + - JAVA_OPTS=-Dserver.port=8858 -Dcsp.sentinel.dashboard.server=localhost:8858 -Dproject.name=sentinel-dashboard + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8858" ] + interval: 30s + timeout: 10s + retries: 3 + networks: + - app_net + restart: always + leaf-server: + image: registry.cn-hangzhou.aliyuncs.com/itheima/meituan-leaf:1.0.1 + container_name: leaf-server + ports: + - "8090:8080" + environment: + - SPRING_PROFILES_ACTIVE=prod + volumes: + - ./leaf/application.properties:/leaf-server/config/application.properties + networks: + - app_net + + zookeeper: + image: bitnami/zookeeper:latest + container_name: zookeeper + ports: + - "2181:2181" + environment: + - ALLOW_ANONYMOUS_LOGIN=yes + networks: + - app_net + restart: always \ No newline at end of file diff --git a/qa-service/nacos_config.sql b/qa-service/nacos_config.sql new file mode 100644 index 0000000000000000000000000000000000000000..433a90b94a37d64ee738e9cc23d4fd82a1663551 --- /dev/null +++ b/qa-service/nacos_config.sql @@ -0,0 +1,179 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/******************************************/ +/* 表名称 = config_info */ +/******************************************/ +CREATE TABLE `config_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) DEFAULT NULL COMMENT 'group_id', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description', + `c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage', + `effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述', + `type` varchar(64) DEFAULT NULL COMMENT '配置的类型', + `c_schema` text COMMENT '配置的模式', + `encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; + +/******************************************/ +/* 表名称 = config_info since 2.5.0 */ +/******************************************/ +CREATE TABLE `config_info_gray` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `src_user` text COMMENT 'src_user', + `src_ip` varchar(100) DEFAULT NULL COMMENT 'src_ip', + `gmt_create` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_create', + `gmt_modified` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_modified', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `gray_name` varchar(128) NOT NULL COMMENT 'gray_name', + `gray_rule` text NOT NULL COMMENT 'gray_rule', + `encrypted_data_key` varchar(256) NOT NULL DEFAULT '' COMMENT 'encrypted_data_key', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfogray_datagrouptenantgray` (`data_id`,`group_id`,`tenant_id`,`gray_name`), + KEY `idx_dataid_gmt_modified` (`data_id`,`gmt_modified`), + KEY `idx_gmt_modified` (`gmt_modified`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='config_info_gray'; + +/******************************************/ +/* 表名称 = config_tags_relation */ +/******************************************/ +CREATE TABLE `config_tags_relation` ( + `id` bigint(20) NOT NULL COMMENT 'id', + `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', + `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识', + PRIMARY KEY (`nid`), + UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation'; + +/******************************************/ +/* 表名称 = group_capacity */ +/******************************************/ +CREATE TABLE `group_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_group_id` (`group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表'; + +/******************************************/ +/* 表名称 = his_config_info */ +/******************************************/ +CREATE TABLE `his_config_info` ( + `id` bigint(20) unsigned NOT NULL COMMENT 'id', + `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `op_type` char(10) DEFAULT NULL COMMENT 'operation type', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥', + `publish_type` varchar(50) DEFAULT 'formal' COMMENT 'publish type gray or formal', + `gray_name` varchar(50) DEFAULT NULL COMMENT 'gray name', + `ext_info` longtext DEFAULT NULL COMMENT 'ext info', + PRIMARY KEY (`nid`), + KEY `idx_gmt_create` (`gmt_create`), + KEY `idx_gmt_modified` (`gmt_modified`), + KEY `idx_did` (`data_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造'; + + +/******************************************/ +/* 表名称 = tenant_capacity */ +/******************************************/ +CREATE TABLE `tenant_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表'; + + +CREATE TABLE `tenant_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `kp` varchar(128) NOT NULL COMMENT 'kp', + `tenant_id` varchar(128) default '' COMMENT 'tenant_id', + `tenant_name` varchar(128) default '' COMMENT 'tenant_name', + `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', + `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', + `gmt_create` bigint(20) NOT NULL COMMENT '创建时间', + `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; + +CREATE TABLE `users` ( + `username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username', + `password` varchar(500) NOT NULL COMMENT 'password', + `enabled` boolean NOT NULL COMMENT 'enabled' +); + +CREATE TABLE `roles` ( + `username` varchar(50) NOT NULL COMMENT 'username', + `role` varchar(50) NOT NULL COMMENT 'role', + UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE +); + +CREATE TABLE `permissions` ( + `role` varchar(50) NOT NULL COMMENT 'role', + `resource` varchar(128) NOT NULL COMMENT 'resource', + `action` varchar(8) NOT NULL COMMENT 'action', + UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE +); + diff --git a/qa-service/pom.xml b/qa-service/pom.xml index 0b70349fcbb4e8f68efabbf1733a3d46456fd39c..7ba8bd5db047647fb746dec45f803f167ed4d017 100644 --- a/qa-service/pom.xml +++ b/qa-service/pom.xml @@ -8,25 +8,37 @@ qa-service qa-service - 17 + 21 UTF-8 UTF-8 - 3.0.2 + 3.2.4 + 2023.0.1.0 + 2023.0.1 + + pom + + + qa-service-bootstrap + qa-service-adapter + qa-service-application + qa-service-domain + qa-service-common + + + - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-test - test - + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + org.springframework.boot spring-boot-dependencies @@ -34,6 +46,19 @@ pom import + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + + com.baomidou + mybatis-plus-spring-boot3-starter + 3.5.14 + @@ -44,8 +69,8 @@ maven-compiler-plugin 3.8.1 - 17 - 17 + 21 + 21 UTF-8 @@ -54,7 +79,7 @@ spring-boot-maven-plugin ${spring-boot.version} - com.example.qa.service.QaServiceApplication + com.example.qa-service.qa-serviceApplication true diff --git a/qa-service/qa-service-adapter/qa-adapter-in/.gitignore b/qa-service/qa-service-adapter/qa-adapter-in/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..549e00a2a96fa9d7c5dbc9859664a78d980158c2 --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-in/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/controller/QAController.java b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/controller/QAController.java new file mode 100644 index 0000000000000000000000000000000000000000..f966d2098ea4f0f4eafa2f03a2cba9c59bd0bc15 --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/controller/QAController.java @@ -0,0 +1,81 @@ +package com.example.qa.adapter.in.web.controller; + +import com.example.application.command.CreateQACommand; +import com.example.application.command.UpdateQACommand; +import com.example.application.port.in.*; +import com.example.qa.adapter.in.web.dto.CreateQARequestDTO; +import com.example.qa.adapter.in.web.dto.QAResponseDTO; +import com.example.qa.adapter.in.web.dto.UpdateQARequestDTO; +import com.example.qa.service.domain.QA; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RequestMapping("/qas") +@RestController +@RequiredArgsConstructor + +public class QAController { + private final CreateQAUseCase createQAUseCase; + private final DeleteQAUseCase deleteQAUseCase; + private final GetQAByIdUseCase getQAByIdUseCase; + private final GetQAListUseCase getQAListUseCase; + private final UpdateQAUseCase updateQAUseCase; + /** + * @author wangqingqing + */ + @GetMapping("") + public List getQAs(){ + return getQAListUseCase.getQAs(); + } + + /** + * @author yangwenkai + * 创建QA的方法 + */ + @PostMapping() + public QA createQA(@RequestBody CreateQARequestDTO createQARequestDTO){ + CreateQACommand command = CreateQACommand.builder() + .question(createQARequestDTO.question()) + .answer(createQARequestDTO.answer()) + .build(); + return createQAUseCase.createQA(command); + } + /** + * @author qiuyujie + */ + @DeleteMapping("{id}") + public String deleteQA(@PathVariable("id") long id){ + deleteQAUseCase.deleteQA(id); + return "success"; + } + /** + * @author lizishan + */ + @PutMapping("") + public QA updateQA(@RequestBody UpdateQARequestDTO updateQARequestDTO){ + UpdateQACommand command= UpdateQACommand.builder() + .id(updateQARequestDTO.id()) + .question(updateQARequestDTO.question()) + .answer(updateQARequestDTO.answer()) + .build(); + QA qa = updateQAUseCase.updateQA(command); + return qa; + } + /** + * @author dengli + */ + @GetMapping("{id}") + public QAResponseDTO getQAById(@PathVariable("id")long id){ + QA qa=getQAByIdUseCase.getQAById(id); + QAResponseDTO qaresponseDTO=new QAResponseDTO( + qa.getId().id(), + qa.getQuestion().question(), + qa.getAnswer().answer() + ); + return qaresponseDTO; + } +} diff --git a/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/CreateQARequestDTO.java b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/CreateQARequestDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..fff74267e4610b0143ccabe8cd7abb18889b9eac --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/CreateQARequestDTO.java @@ -0,0 +1,10 @@ +package com.example.qa.adapter.in.web.dto; + +/** + * @author yangwenkai + * 创建QA的传输数据 + */ +public record CreateQARequestDTO( + String question, + String answer) { +} diff --git a/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/QAResponseDTO.java b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/QAResponseDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..50e781d3e6de8780b3a0a26a6ee399eb1b2dfd03 --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/QAResponseDTO.java @@ -0,0 +1,18 @@ +package com.example.qa.adapter.in.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +/** + * @author yangwenkai + * QA的封装响应参数 + */ +public class QAResponseDTO { + private Long id; + private String question; + private String answer; +} diff --git a/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/UpdateQARequestDTO.java b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/UpdateQARequestDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..b18c0f801d844f1cdf9e700688e5eab2520c8809 --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-in/qa-adapter-in-web/src/main/java/com/example/qa/adapter/in/web/dto/UpdateQARequestDTO.java @@ -0,0 +1,12 @@ +package com.example.qa.adapter.in.web.dto; + +/** + * @author yangwenkai + * 修改QA的传输数据 + */ +public record UpdateQARequestDTO( + long id, + String question, + String answer +) { +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/CreateQABridge.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/CreateQABridge.java new file mode 100644 index 0000000000000000000000000000000000000000..f3fa09d1a7ddece3c0aa5e95b1c8889ac6af7ee1 --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/CreateQABridge.java @@ -0,0 +1,29 @@ +package com.example.qa.adapter.out.persistence.bridge; + +import com.example.qa.adapter.out.persistence.convertor.QAConvertor; +import com.example.qa.adapter.out.persistence.entity.QAEntity; +import com.example.qa.adapter.out.persistence.mapper.QAMapper; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.port.CreateQAPort; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +/** + * @author yangwenkai + * 创建QA的桥接类 + */ +public class CreateQABridge implements CreateQAPort { + @Resource + private QAMapper qAMapper; + + @Override + public QA createQA(QA qa) { + QAEntity qaEntity = QAConvertor.toEntity(qa); + int result = qAMapper.insert(qaEntity); + //result 是受影响的行数 + return qa; + } +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/DeleteQABridge.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/DeleteQABridge.java new file mode 100644 index 0000000000000000000000000000000000000000..014a23060f5c21415a21528d25ade2a325473fbc --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/DeleteQABridge.java @@ -0,0 +1,23 @@ +package com.example.qa.adapter.out.persistence.bridge; + + +import com.example.qa.adapter.out.persistence.mapper.QAMapper; +import com.example.qa.service.domain.port.DeleteQAPort; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +/** + * @author qiuyujie + */ +public class DeleteQABridge implements DeleteQAPort { + @Resource + private QAMapper QAMapper; + @Override + public void deleteQA(Long id) { + int result = QAMapper.deleteById(id); + log.info("result:{}",result); + } +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/GetQAByIdBridge.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/GetQAByIdBridge.java new file mode 100644 index 0000000000000000000000000000000000000000..37bad7c0de514a2c8054ad162cdaa20012f08354 --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/GetQAByIdBridge.java @@ -0,0 +1,25 @@ +package com.example.qa.adapter.out.persistence.bridge; + +import com.example.qa.adapter.out.persistence.convertor.QAConvertor; +import com.example.qa.adapter.out.persistence.entity.QAEntity; +import com.example.qa.adapter.out.persistence.mapper.QAMapper; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.port.GetQAByIdPort; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +/** + * @author dengli + */ +public class GetQAByIdBridge implements GetQAByIdPort { + @Resource + private QAMapper QAMapper; + @Override + public QA getQAById(long id) { + QAEntity QAEntity = QAMapper.selectById(id); + return QAConvertor.toDomain(QAEntity); + } +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/GetQAListBridge.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/GetQAListBridge.java new file mode 100644 index 0000000000000000000000000000000000000000..ebbbd26bee4cd74936a285c9c69bcd37d8ce9f0f --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/GetQAListBridge.java @@ -0,0 +1,36 @@ +package com.example.qa.adapter.out.persistence.bridge; + + +import com.example.qa.adapter.out.persistence.convertor.QAConvertor; +import com.example.qa.adapter.out.persistence.entity.QAEntity; +import com.example.qa.adapter.out.persistence.mapper.QAMapper; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.port.GetQAListPort; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +/** + * @author wangqingqing + */ +public class GetQAListBridge implements GetQAListPort { + + @Resource + private QAMapper QAMapper; + + @Override + public List getQAs() { + List entities = QAMapper.selectList(null); + + ArrayList list = new ArrayList<>(); + + entities.forEach(QAEntity -> { + QA QA = QAConvertor.toDomain(QAEntity); + list.add(QA); + }); + return list; + } +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/UpdateQABridge.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/UpdateQABridge.java new file mode 100644 index 0000000000000000000000000000000000000000..91aecb99cd4424ce909a036d18c2904e92844f1b --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/bridge/UpdateQABridge.java @@ -0,0 +1,26 @@ +package com.example.qa.adapter.out.persistence.bridge; + +import com.example.qa.adapter.out.persistence.convertor.QAConvertor; +import com.example.qa.adapter.out.persistence.mapper.QAMapper; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.port.UpdateQAPort; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +/** + * @author lizishan + */ +public class UpdateQABridge implements UpdateQAPort { + @Resource + private QAMapper QAMapper; + + @Override + public QA updateQA(QA QA) { + int result = QAMapper.updateById(QAConvertor.toEntity(QA)); + log.info("result:{}",result); + return QA; + } +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/convertor/QAConvertor.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/convertor/QAConvertor.java new file mode 100644 index 0000000000000000000000000000000000000000..57df73a0f7a35228cad9f1a7c42a22c7ce676bdf --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/convertor/QAConvertor.java @@ -0,0 +1,29 @@ +package com.example.qa.adapter.out.persistence.convertor; + +import com.example.qa.adapter.out.persistence.entity.QAEntity; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.valueobject.Answer; +import com.example.qa.service.domain.valueobject.QAId; +import com.example.qa.service.domain.valueobject.Question; + +/** + * @author yangwenkai + * QA的数据转换 + */ +public class QAConvertor { + public static QA toDomain(QAEntity qaEntity) { + return new QA( + new QAId(qaEntity.getId()), + new Question(qaEntity.getQuestion()), + new Answer(qaEntity.getAnswer()) + ); + } + + public static QAEntity toEntity(QA qa) { + return new QAEntity( + qa.getId().getValue(), + qa.getQuestion().getValue(), + qa.getAnswer().getValue() + ); + } +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/entity/QAEntity.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/entity/QAEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..82acdb60ce606bec013696b7fb349830255b0cec --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/entity/QAEntity.java @@ -0,0 +1,23 @@ +package com.example.qa.adapter.out.persistence.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("qa") +/** + * @author yangwenkai + * QA的实体类 + */ +public class QAEntity { + @TableId(type = IdType.ASSIGN_ID) + private long id; + private String question; + private String answer; +} diff --git a/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/mapper/QAMapper.java b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/mapper/QAMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..fa3c1c1fea7ac5f8cc5bad9d6a0eb6f385d1fda6 --- /dev/null +++ b/qa-service/qa-service-adapter/qa-adapter-out/qa-adapter-out-persistence/src/main/java/com/example/qa/adapter/out/persistence/mapper/QAMapper.java @@ -0,0 +1,10 @@ +package com.example.qa.adapter.out.persistence.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.qa.adapter.out.persistence.entity.QAEntity; + +/** + * @author lizishan + */ +public interface QAMapper extends BaseMapper { +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/command/CreateQACommand.java b/qa-service/qa-service-application/src/main/java/com/example/application/command/CreateQACommand.java new file mode 100644 index 0000000000000000000000000000000000000000..8688eec43a628d51f383fc0b359e22e62db72e4d --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/command/CreateQACommand.java @@ -0,0 +1,16 @@ +package com.example.application.command; + + +import lombok.Builder; + +@Builder +/** + * @author yangwenkai + * 创建QA的封装参数 + */ +public record CreateQACommand( + Long id, + String question, + String answer +) { +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/command/UpdateQACommand.java b/qa-service/qa-service-application/src/main/java/com/example/application/command/UpdateQACommand.java new file mode 100644 index 0000000000000000000000000000000000000000..b86e3d9f71f1b24b9309db35897ead45c593b9f7 --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/command/UpdateQACommand.java @@ -0,0 +1,15 @@ +package com.example.application.command; + +import lombok.Builder; + +@Builder +/** + * @author yangwenkai + * 修改QA的封装参数 + */ +public record UpdateQACommand ( + long id, + String question, + String answer +) { +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/port/in/CreateQAUseCase.java b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/CreateQAUseCase.java new file mode 100644 index 0000000000000000000000000000000000000000..6960f96b0a23b70e4706c8670e2b1df58162471e --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/CreateQAUseCase.java @@ -0,0 +1,12 @@ +package com.example.application.port.in; + +import com.example.application.command.CreateQACommand; +import com.example.qa.service.domain.QA; + +/** + * @author yangwenkai + * 创建QA的接口 + */ +public interface CreateQAUseCase { + QA createQA(CreateQACommand command); +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/port/in/DeleteQAUseCase.java b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/DeleteQAUseCase.java new file mode 100644 index 0000000000000000000000000000000000000000..3da4cdab76cbc3f0e36e1882e448427d0319e2d9 --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/DeleteQAUseCase.java @@ -0,0 +1,8 @@ +package com.example.application.port.in; + +/** + * @author qiuyujie + */ +public interface DeleteQAUseCase { + void deleteQA(long id); +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/port/in/GetQAByIdUseCase.java b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/GetQAByIdUseCase.java new file mode 100644 index 0000000000000000000000000000000000000000..62b69d560065f33fac0d6d2b3a4b6418e66ac97c --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/GetQAByIdUseCase.java @@ -0,0 +1,10 @@ +package com.example.application.port.in; + +import com.example.qa.service.domain.QA; + +/** + * @author dengli + */ +public interface GetQAByIdUseCase { + QA getQAById(long id); +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/port/in/GetQAListUseCase.java b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/GetQAListUseCase.java new file mode 100644 index 0000000000000000000000000000000000000000..987e33c6a3bf908f9dc13d59d61e98da373cb32a --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/GetQAListUseCase.java @@ -0,0 +1,11 @@ +package com.example.application.port.in; + +import com.example.qa.service.domain.QA; + +import java.util.List; +/** + * @author wangqingqing + */ +public interface GetQAListUseCase { + List getQAs(); +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/port/in/UpdateQAUseCase.java b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/UpdateQAUseCase.java new file mode 100644 index 0000000000000000000000000000000000000000..39a9c6cf1c05797f82ebe44ae8658bc10d404ef9 --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/port/in/UpdateQAUseCase.java @@ -0,0 +1,10 @@ +package com.example.application.port.in; + +import com.example.application.command.UpdateQACommand; +import com.example.qa.service.domain.QA; +/** + * @author lizishan + */ +public interface UpdateQAUseCase { + QA updateQA(UpdateQACommand command); +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/service/CreateQAService.java b/qa-service/qa-service-application/src/main/java/com/example/application/service/CreateQAService.java new file mode 100644 index 0000000000000000000000000000000000000000..2a6a447126e5c459414e2917c9d7880385669798 --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/service/CreateQAService.java @@ -0,0 +1,31 @@ +package com.example.application.service; + +import com.example.application.command.CreateQACommand; +import com.example.application.port.in.CreateQAUseCase; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.port.CreateQAPort; +import com.example.qa.service.domain.valueobject.Answer; +import com.example.qa.service.domain.valueobject.Question; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +/** + * @author yangwenkai + * 创建QA的实现类 + */ +public class CreateQAService implements CreateQAUseCase { + @Resource + private CreateQAPort createQAPort; + + @Override + public QA createQA(CreateQACommand createQACommand) { + QA qa = new QA( + new Question(createQACommand.question()), + new Answer(createQACommand.answer()) + ); + return createQAPort.createQA(qa); + } +} diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/service/DeleteQAService.java b/qa-service/qa-service-application/src/main/java/com/example/application/service/DeleteQAService.java new file mode 100644 index 0000000000000000000000000000000000000000..1d8fab5e755a6a45953c67cad6ea9804f1614a52 --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/service/DeleteQAService.java @@ -0,0 +1,20 @@ +package com.example.application.service; + +import com.example.application.port.in.DeleteQAUseCase; +import com.example.qa.service.domain.port.DeleteQAPort; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +@Service +/** + * @author qiuyujie + */ +public class DeleteQAService implements DeleteQAUseCase { + @Resource + private DeleteQAPort deleteQAPort; + + @Override + public void deleteQA(long id) { + deleteQAPort.deleteQA(id); + } +} \ No newline at end of file diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/service/GetQAByIdService.java b/qa-service/qa-service-application/src/main/java/com/example/application/service/GetQAByIdService.java new file mode 100644 index 0000000000000000000000000000000000000000..b224f7b67f7842f87a07352e614462e5982bf548 --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/service/GetQAByIdService.java @@ -0,0 +1,22 @@ +package com.example.application.service; + +import com.example.application.port.in.GetQAByIdUseCase; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.port.GetQAByIdPort; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +@Service +/** + * @author dengli + */ +public class GetQAByIdService implements GetQAByIdUseCase { + + @Resource + private GetQAByIdPort getQAByIdPort; + @Override + public QA getQAById(long id) { + return getQAByIdPort.getQAById(id); + } +} + diff --git a/qa-service/qa-service-application/src/main/java/com/example/application/service/GetQAListService.java b/qa-service/qa-service-application/src/main/java/com/example/application/service/GetQAListService.java new file mode 100644 index 0000000000000000000000000000000000000000..fb72ee69136babd56571114fec1970e5b1869d47 --- /dev/null +++ b/qa-service/qa-service-application/src/main/java/com/example/application/service/GetQAListService.java @@ -0,0 +1,23 @@ +package com.example.application.service; + +import com.example.application.port.in.GetQAListUseCase; +import com.example.qa.service.domain.QA; +import com.example.qa.service.domain.port.GetQAListPort; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; +/** + * @author wangqingqing + */ +@Service +public class GetQAListService implements GetQAListUseCase { + + @Resource + GetQAListPort getQAListPort; + @Override + public List getQAs() { + List 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 0000000000000000000000000000000000000000..d908f68854c34ca7f754bb93f0b71e72dabcd597 --- /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-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 0000000000000000000000000000000000000000..ac96e23534a47e45c5d6fb06dbe2a4b3838df792 --- /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 0000000000000000000000000000000000000000..c40b3bb8b50adb5ed894a91f9236691c5fa3da7b --- /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 0000000000000000000000000000000000000000..aeb293a24a7b7f71effce1863e805726e786cf58 --- /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 0000000000000000000000000000000000000000..2ea8e475eb4d2f5e5df59009a0a16960b9b998ff --- /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 0000000000000000000000000000000000000000..fdc3b92c6866260c51aaeeaa7d8a5bc90486037b --- /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 0000000000000000000000000000000000000000..3fc891d5e12b7ecac4e4b6afdba25780db31b214 --- /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 0000000000000000000000000000000000000000..5713b796ac0f3a0ec2fc46c8336bcfcc2357ede6 --- /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 0000000000000000000000000000000000000000..8a34b3c6566dccc0ca7ffc28636c7f2a0de8e817 --- /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 0000000000000000000000000000000000000000..a423600d57ed7a90faf87eace687a414c0b76d37 --- /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 0000000000000000000000000000000000000000..773a8f95b7ddc44f57555c60fc3f4ff3b5d71a60 --- /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; + } +} 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 e5e3e37fef4e8839ffb9c6bf9ca9b71b43003bbf..0de0058917c0c373427ab2fe0a7948c6baea327d 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; @@ -21,10 +23,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 +56,58 @@ 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(); } + @Resource + private GetUserByIdPort getUserByIdPort; + + // 静态字段注入 - 用于静态方法 + private static GetUserByNamePort staticGetUserByNamePort; + + // 静态注入方法 - 由Spring容器调用 + @Resource + public void setStaticGetUserByNamePort(GetUserByNamePort getUserByNamePort) { + User.staticGetUserByNamePort = getUserByNamePort; + } - public static User getUserByName(String name, GetUserByNamePort getUserByNamePort){ - User user = getUserByNamePort.getUserByName(name,getUserByNamePort); + // 静态方法 - 只使用name参数,通过静态字段获取依赖 + public static User getUserByName(String name){ + if (staticGetUserByNamePort == null) { + throw new IllegalStateException("GetUserByNamePort依赖未注入,请确保Spring容器已初始化"); + } + User user = staticGetUserByNamePort.getUserByName(name); return user; }