В запросе скорее всего текст технической направленности из области разработки и тестирования программного обеспечения. Основной язык текста запроса — китайский.
Установить otp_win64_20.2.exe.
Установить rabbitmq-server-3.7.4.exe.
Настроить RabbitMQ клиентскую среду.
Настроить RabbitMQ серверную среду.
В среде переменных добавить RabbitMQ сервер.
Установить плагин: rabbitmq-plugins.bat enable rabbitmq_management. Перезапустить RabbitMQ сервис. Запустить RabbitMQ.
Сначала посмотрим на структуру каталога секундного убийства.
Шаг 1: Выберите File-New-Module... В появившемся окне выберите Spring initializr, выберите Module SDK, выберите Next.
Шаг 2: Group для com.itheima, Artifact для leyou, Name для leyouServer, Description для Server For leyou Project, выберите Next.
Шаг 3: Выберите Spring Cloud Discovery, выберите Eureka Server, выберите Next.
Шаг 4: Module name — leyouServer, Content root — путь + leyouServer, выберите Finish, в этот момент в папке проекта будет создана папка leyouServer.
Откройте pom.xml в сгенерированном проекте и настройте зависимости.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Импортируйте изменения в Maven. В правом нижнем углу всплывающего окна нажмите Import Changes, Maven автоматически загрузит необходимые jar-файлы из удалённого репозитория. Если импорт не удался, вы можете следовать инструкциям ниже:
Откройте src\man\resources\application.properties в сгенерированном проекте и настройте порт и другие параметры.
server.port=9000
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
eureka.instance.hostname=localhost
spring.application.name=leyou-server
# 不从服务器拿服务信息
eureka.client.fetch-registry=false
# 不在服务端注册
eureka.client.register-with-eureka=false
``` ### Тестирование программного обеспечения
1. **Тестовый запуск сервиса товаров.** В регистрационном центре Eureka можно увидеть сервис leyou-Stock, что свидетельствует об успешном запуске сервиса.
2. **Создание папок controller, service и dao, а также файлов StockController.java, StockService.java и StockDao.java.**
* Controller отвечает за управление бизнес-процессами (Service) и управление переходами.
* Service отвечает за конкретные функции и ветвление решений.
* Dao отвечает за взаимодействие с данными и выполнение операций добавления, изменения, удаления и запроса данных.
3. **Контроллер похож на официанта**, который принимает заказ от клиента и направляет его к определённому столику.
4. **Сервис похож на повара**, который готовит блюда из меню, полученного от клиента.
5. **Dao похож на кухонного работника**, который работает с исходными материалами.
6. **Создание метода для получения списка товаров (getStockList).**
* Метод используется для предоставления данных о товарах на страницу переднего плана.
* Сначала необходимо определить структуру данных для обмена с передним планом:
* Если возвращаемое значение неверно, то {"result":"false", "msg":"****"}.
* Если возвращаемое значение верно, то {"result":"true", "msg":"","sku_list":["id":1,"sku_id":...]}.
* Затем нужно создать интерфейс IStockDao и реализовать его в классе StockDao.
* Далее следует добавить аннотацию @Repository для обозначения компонента доступа к данным (DAO).
* Необходимо объявить метод JDBCTemplate для подключения к базе данных.
* Нужно создать метод getStockList, который будет получать данные о товарах из базы данных и помещать их в список ArrayList. Затем этот список будет возвращён.
7. **Затем нужно написать код для сервиса.**
* Создаётся интерфейс IStockService.
* Класс StockService реализует интерфейс IStockService и получает аннотацию @Service.
* В класс StockService добавляется ссылка на интерфейс IStockDao.
* Добавляется метод getStockList, который вызывает метод getStockList из класса StockDao и возвращает карту Map.
8. **Создаётся метод getStock для класса StockDao.** Этот метод принимает параметр sku_id и выполняет запрос к базе данных, чтобы получить информацию о товаре по этому идентификатору.
9. **В классе StockService создаётся метод getStock,** который возвращает карту с информацией о товаре.
10. **Для тестирования метода getStockList** используется URL http://localhost:7000/getStockList. Вот перевод текста на русский язык:
policyMap){ //1、判断传入的参数是不是合法 Map<String, Object> resultMap = new HashMap<String, Object>(); if (policyMap==null||policyMap.isEmpty()){ resultMap.put("result", false); resultMap.put("msg", "传入什么东东"); return resultMap; }
//2、从StockDao接口中调用insertLimitPolicy方法
boolean result = iStockDao.insertLimitPolicy(policyMap);
//3、判断执行成功或失败,如果失败,返回错误信息
if (!result){
resultMap.put("result", false);
resultMap.put("msg", "数据执行咋又失败了");
return resultMap;
}
//4、如果成功,写入redis,需要写入有效期,key取名:LIMIT_POLICY_{sku_id}
long diff = 0;
String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
//结束日期减去当前日期得到政策的有效期
try {
Date end_time = simpleDateFormat.parse(policyMap.get("end_time").toString());
Date now_time = simpleDateFormat.parse(now);
diff = (end_time.getTime() - now_time.getTime())/1000;
if (diff<0){
resultMap.put("result", false);
resultMap.put("msg", "结束时间不能小于当前时间");
return resultMap;
}
} catch (ParseException e) {
resultMap.put("result", false);
resultMap.put("msg", "日期转换又失败了");
return resultMap;
}
String policy = JSON.toJSONString(policyMap);
stringRedisTemplate.opsForValue().set("LIMIT_POLICY_"+policyMap.get("sku_id").toString(), policy, diff, TimeUnit.SECONDS);
ArrayList<Map<String, Object>> list = iStockDao.getStock(policyMap.get("sku_id").toString());
String sku = JSON.toJSONString(list.get(0));
stringRedisTemplate.opsForValue().set("SKU_"+policyMap.get("sku_id").toString(), sku, diff, TimeUnit.SECONDS);
//5、返回正常信息
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
**注意:这里的Service有业务逻辑判断,首先判断传入的json是否可以转化成功,其次是写入数据库成功或者失败。**
**Здесь добавлен @Transactional, который требует открытия транзакции. Необходимо помнить, что только три оператора insert\delete\update требуют открытия транзакции, и если запись в базу данных не удалась, можно откатить. Поскольку вышеописанные методы являются запросами, нет необходимости открывать транзакцию.**
В StockController добавлен метод insertLimitPolicy, требующий добавления аннотации @RequestMapping, где value = "/insertLimitPolicy/{jsonObj}", возвращающий товар Map, код следующий:
```java
@RequestMapping(value = "/insertLimitPolicy/{jsonObj}")
public Map<String, Object> insertLimitPolicy(@PathVariable("jsonObj") String jsonObj){
return iStockService.insertLimitPolicy(jsonObj);
}
Для тестирования метода insertLimitPolicy введите в браузере http://localhost:7000/insertLimitPolicy/{sku_id:'26816294479',quanty:1000,price:1000,begin_time:'2019-08-05 11:00',end_time:'2019-10-05 12:00'}.
Шаги:
Код следующий:
Объявление StringRestTemplate:
@Autowired
StringRedisTemplate stringRedisTemplate;
Изменение метода insertLimitPolicy в StockService с добавлением следующего кода:
Шаги:
Метод упаковки: getLimitPolicy
Код следующий:
private Map<String, Object> getLimitPolicy(ArrayList<Map<String, Object>> list){
Map<String, Object> resultMap = new HashMap<String, Object>();
for (Map<String, Object> skuMap: list){
//3.1、Извлечь политику из redis
String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+skuMap.get("sku_id").toString());
//3.2、Только если есть политика, продолжить
if (policy!=null&&!policy.equals("")){
Map<String, Object> policyInfo = JSONObject.parseObject(policy, Map.class);
//3.3、Если начало времени меньше или равно текущему времени, а текущее время меньше или равно концу времени
``` Вот перевод текста на русский язык:
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class); try { Date end_time = simpleDateFormat.parse(policyInfo.get("end_time").toString()); Date begin_time = simpleDateFormat.parse(policyInfo.get("begin_time").toString()); Date now_time = simpleDateFormat.parse(now);
if (begin_time.getTime() <= now_time.getTime() && now_time.getTime() <= end_time.getTime()) {
skuMap.put("limitPrice", policyInfo.get("price"));
skuMap.put("limitQuanty", policyInfo.get("quanty"));
skuMap.put("limitBeginTime", policyInfo.get("begin_time"));
skuMap.put("limitEndTime", policyInfo.get("end_time"));
skuMap.put("nowTime", now);
}
} catch (ParseException e) { e.printStackTrace(); } } resultMap.put("result", true); resultMap.put("msg", ""); return resultMap; }
Здесь нужно обратить внимание: политика записывается в список и передаётся на переднюю страницу.
* limitPrice: цена политики.
* limitBeginTime: время начала политики.
* limitEndTime: время окончания политики.
* nowTime: текущее время.
## 4.4 Создание сервиса для управления запасами (порт номер 6001)
------
**Обратите внимание: здесь конфигурация порта номер не 6000, а 6001, причина в том, что порт 6000 недоступен в браузере Chrome Google.**
### 4.4.1 Создание структуры таблицы запасов
1. Таблица склада:
```sql
DROP TABLE IF EXISTS `tb_warehouse`;
CREATE TABLE `tb_warehouse` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'идентификатор склада',
`name` VARCHAR(64) NOT NULL COMMENT 'название склада',
`create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'время создания',
`update_time` TIMESTAMP NULL COMMENT 'время обновления',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
Назначение: используется для хранения остатков запасов.
DROP TABLE IF EXISTS `tb_stock_storage`;
CREATE TABLE `tb_stock_storage` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`warehouse_id` BIGINT(20) NOT NULL COMMENT 'идентификатор склада',
`sku_id` BIGINT(20) NOT NULL COMMENT 'sku id',
`quanty` DECIMAL(18,2) COMMENT 'остаток количества',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
Назначение: используется для записи истории движения запасов.
DROP TABLE IF EXISTS `tb_stock_storage_history`;
CREATE TABLE `tb_stock_storage_history` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`stock_storage_id` BIGINT(20) NOT NULL COMMENT 'идентификатор основной таблицы запасов',
`in_quanty` DECIMAL(18,2) COMMENT 'количество поступления',
`out_quanty` DECIMAL(18,2) COMMENT 'количество выбытия',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
Правила записи запасов:
Примечание: в основной таблице запасов один склад соответствует одному товару, и хранится только одна запись, в которой quanty хранит остаток количества.
В таблице истории запасов каждый склад имеет соответствующую основную таблицу запасов, которая записывает историю движения количества каждого товара.
Объяснение правил записи:
Определить наличие данных в основной таблице запасов по sku_id;
Если есть данные, получить идентификатор основной таблицы запасов;
Если нет данных, сначала записать в основную таблицу запасов и получить идентификатор основной таблицы запасов;
Записать в таблицу истории запасов на основе идентификатора основной таблицы запасов;
Когда в основной таблице запасов есть данные, обновить количество на основе идентификатора основной таблицы запасов.
Первый шаг: выберите File-New-Module..., появится окно, выберите Spring initializr, выберите Module SDK, выберите Next
Второй шаг: Group — com.itheima, Artifact — leyou, Name — leyouStorage, Description — Storage For leyou Project, выберите Next
Третий шаг: Выберите Spring Cloud Discovery, выберите Eureka Discovery Client, выберите Next
Четвёртый шаг: Module name — leyouStorage, Content root — путь + leyouStorage, выберите Finish, после чего в папке проекта будет создана папка leyouStorage.
В созданном проекте откройте pom.xml, настройте зависимости, где spring-cloud-starter-netflix-eureka-client — это jar-пакет, который вводит клиент Eureka в проект, spring-boot-starter-web — это jar-пакеты, используемые при разработке веб-модулей, alibaba.fastjson — это jar-пакеты, которые используют fastjson от Alibaba для анализа данных json, mysql-connector-java и spring-boot-starter-data-jpa — это зависимости, которые вводят jar-пакеты для подключения к MySQL. Конфигурационный файл application.properties
В созданном проекте откройте файл src\main\resources\application.properties и настройте номер порта и другие параметры:
server.port=6001
spring.application.name=leyou-storage
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/code1_2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
Добавление аннотации @EnableEurekaClient в класс StorageApplication
В созданном проекте откройте файл src\main\java\com.itheima.leyou\leyouStorageApplication. В уже существующий класс StorageApplication добавьте аннотацию @EnableEurekaClient, чтобы запустить Eureka клиент:
@SpringBootApplication
@EnableEurekaClient
public class StorageApplication {
public static void main(String[] args) {
SpringApplication.run(StorageApplication.class, args);
}
}
Создание папок controller, service и dao, а также файлов StorageController.java, StorageService.java и StorageDao.java
Создайте интерфейс IStorageDao:
public interface IStorageDao {}
Добавьте аннотацию @Repository к классу StorageDao, чтобы пометить его как компонент доступа к данным (DAO):
@Repository
public class StorageDao implements IStorageDao{}
Объявите метод JDBCTemplate для подключения к базе данных:
@Autowired
private JdbcTemplate jdbcTemplate;
Создайте интерфейс IStorageService:
@Service
public class StorageService implements IStorageService {}
Внедрите интерфейс IStorageDao в класс StorageService:
@Autowired
private IStorageDao iStorageDao;
Добавьте аннотации к классу StorageController:
@RestController
@Configuration
public class StorageController {}
Внедрите интерфейс IStorageService в класс StorageController:
@Autowired
private IStorageService iStorageService;
Метод getStockStorage для запроса фактического количества товаров на складе
Метод getStockStorage в классе StorageDao принимает параметр sku_id и возвращает список товаров:
public ArrayList<Map<String, Object>> getStockStorage(String sku_id){
//1、SQL запрос
String sql = "SELECT sku_id, quanty FROM tb_stock_storage WHERE sku_id = ?";
//2、Возвращаем данные
return (ArrayList<Map<String,Object>>) jdbcTemplate.queryForList(sql, sku_id);
}
Метод getStockStorage в классе StorageService возвращает карту товаров:
public Map<String, Object> getStockStorage(String sku_id){
//1, Получаем количество товара на складе
ArrayList<Map<String ,Object>>
``` **Версия 1.0**
Кодировка UTF-8
Проект Maven
```xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouUser</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Конфигурация файла application.properties
В сгенерированном проекте откройте src\main\resources\application.properties, настройте номер порта и другие параметры.
server.port=5000
spring.application.name=leyou-user
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/code1_2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#redis数据库编号,существует 0~15, всего 16 баз данных
spring.redis.database=0
#redis сервер IP
spring.redis.host=127.0.0.1
#redis порт
spring.redis.port=6379
#redis пароль
spring.redis.password=leyou
#redis запрос тайм-аут, превышающий это значение, redis автоматически отключит соединение
spring.redis.timeout=10000ms
#jedis максимальное количество подключений, превышение этого значения вызовет исключение «невозможно получить подключение»
spring.redis.jedis.pool.max-active=32
#jedis максимальный период ожидания, превышение которого вызовет исключение «тайм-аут подключения»
spring.redis.jedis.pool.max-wait=10000ms
#jedis максимальное время простоя соединения
spring.redis.jedis.pool.max-idle=32
#jedis минимальное время простоя соединения
spring.redis.jedis.pool.min-idle=0
В сгенерированном проекте откройте файл src\main\java\com.itheima.leyou\leyouUserApplication и добавьте аннотацию @EnableEurekaClient в уже созданный класс запуска UserApplication. Это означает запуск Eureka Client.
@SpringBootApplication
@EnableEurekaClient
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
Протестируйте запуск службы заказов. В центре регистрации Eureka можно увидеть службу leyou-User, что свидетельствует об успешном запуске службы.
Создание папок controller, service и dao, а также файлов UserController.java, UserService.java и UserDao.java
Создайте интерфейс Dao слоя IUserDao.
public interface IUserDao {}
Добавьте аннотации к классу UserDao.java для реализации интерфейса IUserDao и использования @Repository.
@Repository
public class UserDao implements IUserDao {}
Объявите метод JDBCTemplate для подключения к базе данных.
@Autowired
private JdbcTemplate jdbcTemplate;
Создайте сервисный слой интерфейса IUserService.
Добавьте аннотацию к классу UserService.java для реализации IUserService интерфейса. Реализует IUserService{}
Присоединить ссылку на UserDao
```java
@Autowired
private IUserDao iUserDao;
Добавить аннотацию к классу UserController.java
@RestController
public class UserController {}
Присоединить ссылку на интерфейс IUserService
@Autowired
private IUserService iUserService;
Назначение: предоставить данные информации о члене для внешнего интерфейса, в основном используется для входа члена во внешний интерфейс
В классе UserDao.java добавить метод getUser с двумя параметрами username и password, что означает поиск по имени пользователя и паролю и возврат списка членов. Код выглядит следующим образом:
public ArrayList<Map<String, Object>> getUser(String username, String password){
String sql = "select id AS user_id, username, phone, password from tb_user where username = ?";
return (ArrayList<Map<String,Object>>) jdbcTemplate.queryForList(sql, username);
}
В классе UserService.java добавить метод getUser, который возвращает список товаров. Код выглядит следующим образом:
public Map<String, Object> getUser(String username, String password){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、Проверить правильность переданных параметров
if (username==null||username.equals("")){
resultMap.put("result", false);
resultMap.put("msg", "Имя пользователя не может быть пустым!");
return resultMap;
}
//2、Получить список членов
ArrayList<Map<String, Object>> list = iUserDao.getUser(username, password);
if (list==null||list.isEmpty()){
resultMap.put("result", false);
resultMap.put("msg", "Информация о члене не найдена!");
return resultMap;
}
resultMap = list.get(0);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
Назначение: Предоставить информацию о добавлении члена для внешнего интерфейса, в основном используется при регистрации члена на внешнем интерфейсе
В классе UserDao.java добавьте метод insertUser с тремя параметрами username, phone и password. Возвращает карту. Код выглядит следующим образом:
public int insertUser(String username, String password){
final String sql = "insert into tb_user (username, phone, password) values ('"+username+"', '"+username+"', '"+password+"')";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
return preparedStatement;
}
}, keyHolder);
return keyHolder.getKey().intValue();
}
Анализ кода:
В классе UserService.java добавьте метод insertUser, возвращающий карту товаров. Код выглядит следующим образом:
@Transactional
public Map<String, Object> insertUser(String username, String password){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、Проверка правильности переданных параметров
if (username==null||username.equals("")){
resultMap.put("result", false);
resultMap.put("msg", "Имя пользователя не может быть пустым!");
return resultMap;
}
int user_id = iUserDao.insertUser(username, password);
if (user_id<=0){
resultMap.put("result", false);
resultMap.put("msg", "Операция базы данных не выполнена успешно!");
return resultMap;
}
resultMap.put("user_id", user_id);
resultMap.put("username", username);
resultMap.put("phone", username);
resultMap.put("password", password);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
Примечание: В этом сервисе есть бизнес-логика, если преобразование Json неверно или переданные параметры пусты, это может привести к ошибке записи базы данных, поэтому перед передачей в слой Dao необходимо выполнить проверку. Разобранная json также должна быть проверена, имя пользователя и номер телефона не могут быть пустыми.
Необходимо добавить аннотацию @RequestMapping, где значение = "/login", и вернуть карту. Код выглядит следующим образом:
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Map<String, Object> login(String username, String password, HttpServletRequest httpServletRequest){
//1、Получение информации о пользователе
Map<String, Object> userMap = new HashMap<String, Object>();
userMap = iUserService.getUser(username, password);
//2、Не удалось получить информацию о пользователе, записать информацию о пользователе
if (!(Boolean) userMap.get("result")){
userMap = iUserService.insertUser(username, password);
}
//3、Запись в сеанс
HttpSession httpSession = httpServletRequest.getSession();
String user = JSON.toJSONString(userMap);
httpSession.setAttribute("user", user);
Object o = httpSession.getAttribute("user");
//4、Возврат информации
return userMap;
}
Анализ кода:
— Логин: использование методов getUser и insertUser
Логин использует методы getUser для поиска пользователя по данным, введённым на фронтенде (телефон и пароль), и insertUser для регистрации нового пользователя.
В реальных проектах обычно есть два этапа проверки:
После успешной авторизации данные о пользователе записываются в сессию.
4.6. Создание сервиса заказов (порт 4000)
4.6.1. Структура таблиц для заказов
Основная таблица заказов: содержит информацию о заказе, такую как общая сумма, данные о клиенте, способ оплаты и время оплаты.
Таблица деталей заказа: связана с основной таблицей и содержит подробную информацию о каждом элементе заказа, например, количество, цену и общую стоимость каждого товара.
Таблица состояния доставки заказа: связана с основной таблицей, содержит информацию о статусе доставки каждого элемента заказа, включая компанию-перевозчика, статус доставки и подтверждение получения.
4.6.2. Создание сервиса заказов
Шаги для создания сервиса:
После выполнения этих шагов в папке проекта будет создана папка leyouOrder.
4.6.3. Конфигурация pom.xml
В созданном проекте открыть файл pom.xml и настроить зависимости.
spring-cloud-starter-netflix-eureka-client — зависимость для использования Eureka клиента. spring-boot-starter-web — зависимость для разработки веб-модулей. alibaba.fastjson — зависимость для работы с JSON данными. mysql-connector-java и spring-boot-starter-data-jpa — зависимости для подключения к MySQL базе данных.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouOrder</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.6.4. Конфигурация файла application.properties
Открыть файл src\man\resources\application.properties и настроить порт и другие параметры.
server.port=4000
spring.application.name=leyou-order
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/code1_2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#redis数据库编号, существует 0~15, всего 16 баз данных
spring.redis.database=0
#redis服务器IP
spring.redis.host=127.0.0.1
#redis端口号
spring.redis.port=6379
#redis密码
``` **spring.redis.password=leyou**
# redis запрос тайм-аут, если превышено это значение, redis автоматически разорвёт соединение
**spring.redis.timeout=10000ms**
# максимальное количество соединений jedis, если превышено, будет выдано исключение о недоступности соединения
**spring.redis.jedis.pool.max-active=32**
# максимальный тайм-аут jedis, превышение вызовет исключение тайм-аута соединения
**spring.redis.jedis.pool.max-wait=10000ms**
# максимальное число неактивных соединений jedis
**spring.redis.jedis.pool.max-idle=32**
# минимальное число неактивных соединений jedis
**spring.redis.jedis.pool.min-idle=0**
В созданном проекте откройте файл src\main\java\com.itheima.leyou\leyouOrderApplication и в уже существующем классе запуска добавьте @EnableEurekaClient для запуска Eureka-клиента.
```java
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
После тестирования запуска службы заказа в центре регистрации Eureka можно увидеть сервис leyou-Order, что свидетельствует об успешном запуске службы.
Создайте файл интерфейса Dao слоя IOrderDao.
public interface IOrderDao{}
Добавьте аннотацию @Repository к классу OrderDao.java для обозначения компонента доступа к данным (DAO).
@Repository
public class OrderDao implements IOrderDao {}
Объявите метод JDBCTemplate для подключения к базе данных.
@Autowired
JdbcTemplate jdbcTemplate;
Создайте интерфейс Service слоя IOrderService.
Добавьте аннотацию @Service к классу OrderService.java для реализации интерфейса IOrderService.
@Service
public class OrderService implements IOrderService {}
Включите ссылку на IOrderDao.
@Autowired
private IOrderDao iOrderDao;
Добавьте аннотацию @RestController к классу OrderController.java.
Включите ссылку на IOrderService.
@Autowired
private IOrderService iOrderService;
Этот метод в основном используется для вызова со страницы отправки заказа на переднем конце и записи заказа в очередь.
Обратите внимание: здесь необходимо вернуть order_id, который будет использоваться страницей оплаты для запроса.
public Map<String, Object> createOrder(String sku_id, String user_id){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、проверка sku_id
if (sku_id==null||sku_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", "Ошибка передачи данных с переднего конца!");
return resultMap;
}
//2、получение политики из redis
String order_id = String.valueOf(System.currentTimeMillis());
String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+sku_id);
if (policy!=null&&!policy.equals("")){
//3、начало времени меньше или равно текущему времени, текущий момент времени меньше или равен концу
Map<String, Object> policyMap = JSONObject.parseObject(policy, Map.class);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
try {
Date begin_time = simpleDateFormat.parse(policyMap.get("begin_time").toString());
Date end_time = simpleDateFormat.parse(policyMap.get("end_time").toString());
Date now_time = simpleDateFormat.parse(now);
if (begin_time.getTime()<=now_time.getTime()&&now_time.getTime()<=end_time.getTime()){
int limitQuanty = Integer.parseInt(policyMap.get("quanty").toString());
//4、счётчик redis
// +1+1+1=3 4
if (stringRedisTemplate.opsForValue().increment("SKU_QUANTY_"+sku_id, 1)<=limitQuanty){
//5、запись в очередь
// tb_order: order_id, total_fee, actual_fee, post_fee, payment_type, user_id, status, create_time
// tb_order_detail: order_id, sku_id, num, title, own_spec, price, image, create_time
// tb_sku: sku_id, title, images, stock, price, indexes, own_spec
String sku = stringRedisTemplate.opsForValue().get("SKU_"+sku_id);
Map<String, Object> skuMap = JSONObject.parseObject(sku, Map.class);
Map<String, Object> orderInfo = new HashMap<String, Object>();
orderInfo.put("order_id", order_id);
orderInfo.put("total_fee", skuMap.get("price"));
orderInfo.put("actual_fee", policyMap.get("price"));
orderInfo.put("post_fee", 0);
orderInfo.put("payment_type", 0);
orderInfo.put("user_id", user_id);
orderInfo.put("status", 1);
orderInfo.put("create_time", now);
``` **В запросе представлен код на языке Java.**
**Текст запроса:**
orderInfo.put("sku_id", skuMap.get("sku_id")); orderInfo.put("num", 1); orderInfo.put("title", skuMap.get("title")); orderInfo.put("own_spec", skuMap.get("own_spec")); orderInfo.put("price", policyMap.get("price")); orderInfo.put("image", skuMap.get("images"));
String order = JSON.toJSONString(orderInfo);
try {
amqpTemplate.convertAndSend("order_queue", order);
}catch (Exception e){
resultMap.put("result", false);
resultMap.put("msg", "写入队列异常!");
return resultMap;
}
}else { //如果超出了计数器,返回商品已经售完了 resultMap.put("result", false); resultMap.put("msg", "3亿9被踢回去了!"); return resultMap; }
**Перевод текста на русский язык:**
orderInfo.put("sku_id", skuMap.get("sku_id")); orderInfo.put("num", 1); orderInfo.put("title", skuMap.get("title")); orderInfo.put("own_spec", skuMap.get("own_spec")); orderInfo.put("price", policyMap.get("price")); orderInfo.put("image", skuMap.get("images"));
String order = JSON.toJSONString(orderInfo);
try {
amqpTemplate.convertAndSend("order_queue", order);
} catch (Exception e) {
resultMap.put("result", false);
resultMap.put("msg", "Ошибка при записи в очередь!");
return resultMap;
}
} else {
//если счётчик превышен, товар распродан
resultMap.put("result", false);
resultMap.put("msg", "Товар 39 распродан!");
return resultMap;
}
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
В папке src\main\java\com\itheima\leyou создайте папку queue, а в ней — файл OrderQueue.java.
@Component
public class OrderQueue {
@Autowired
private IOrderService iOrderService;
@RabbitListener(queues = "order_queue")
public void insertOrder(String msg){
//1、получаем сообщение и выводим его
System.out.println("order_queue получил сообщение: " + msg);
//2、вызываем метод записи заказа
Map<String, Object> orderInfo = JSONObject.parseObject(msg, Map.class);
Map<String, Object> resultMap = new HashMap<String, Object>();
resultMap = iOrderService.insertOrder(orderInfo);
//3、если запись не удалась, выводим сообщение об ошибке
if (!(Boolean) resultMap.get("result")){
System.out.println("Обработка сообщения order_queue завершилась неудачно:");
}
//4、в случае успеха выводим сообщение
System.out.println("Сообщение order_queue обработано успешно!");
}
}
Сначала приостановите проверку сеанса и присвойте значение user_id равное 1.
Введите в браузере http://localhost:4000/createOrder/26816294479.
Очередь также успешно обработана.
Назначение: запрос информации о заказе для использования в разделе «Мой заказ» на сайте.
Структура таблицы заказов: основная таблица заказов, таблица деталей заказов, таблица состояния заказов. Один заказ имеет одну основную таблицу, несколько деталей и несколько состояний.
Добавьте метод getOrder в класс OrderDao.java, который принимает параметр orderid и возвращает список заказов в виде карты. Код выглядит следующим образом:
public ArrayList<Map<String, Object>> getOrder(String order_id){
String sql = "select d.sku_id, m.order_id, d.price " +
"from tb_order m inner join tb_code d on m.order_id = d.order_id " +
"where m.order_id = ?";
return (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql, order_id);
}
Добавьте метод getOrder в классе StorageService.java, возвращающий карту товаров. Код следующий:
public Map<String, Object> getOrder(String order_id){
Map<String, Object> resultMap = new HashMap<String, Object>();
if (order_id==null||order_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", "Некорректный ввод параметра!");
return resultMap;
}
ArrayList<Map<String, Object>> list = iOrderDao.getOrder(order_id);
resultMap.put("order", list);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
Добавьте метод getOrder в классе StorageController.java с аннотацией @RequestMapping, где value = "/getOrder/{orderid}", и верните карту товаров. Код такой:
@RequestMapping(value = "/getOrder/{order_id}")
public Map<String, Object> getOrder(@PathVariable("order_id") String order_id){
return iOrderService.getOrder(order_id);
}
Основное применение: метод обновления очереди для изменения статуса заказа. В классе OrderDao.java добавьте метод updateOrderStatus. Код:
public boolean updateOrderStatus(String order_id){
String sql = "update tb_order set status = 2 where order_id = ?";
return jdbcTemplate.update(sql, order_id)==1;
}
Основная цель: вызов из интерфейса оплаты и обновление статуса заказа в очереди.
В классе OrderService.java добавьте метод payOrder.
public Map<String, Object> payOrder(String order_id, String sku_id){
Map<String, Object> resultMap = new HashMap<String, Object>();
if (order_id==null||order_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", "Ошибка в заказе!");
return resultMap;
}
boolean result = iOrderDao.updateOrderStatus(order_id);
if (!result){
resultMap.put("result", false);
resultMap.put("msg", "Обновление статуса заказа не удалось!");
return resultMap;
}
amqpTemplate.convertAndSend("storage_queue", sku_id);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
В классе OrderController.java добавьте метод payOrder:
@RequestMapping(value = "/payOrder/{order_id}/{sku_id}")
public Map<String, Object> payOrder(@PathVariable("order_id") String order_id, @PathVariable("sku_id") String sku_id){
//В нормальных условиях здесь будет вызываться интерфейс оплаты, но мы имитируем успешный возврат данных
boolean isPay = true;
Map<String, Object> resultMap = new HashMap<String, Object>();
if (!isPay){
resultMap.put("result", false);
resultMap.put("msg", "Сбой вызова интерфейса оплаты!");
return resultMap;
}
return
``` **4.6.13. Тестирование запроса на заказ**
Прямой доступ к странице через метод getOrder, http://localhost:4000/getOrder/1565019341112.
**4.7. Создание шлюза сервиса (порт 80)**
------
**4.7.1. Создание шлюза сервиса**
*Шаг 1.* Выберите File-New-Module... В появившемся окне выберите Spring initializr, выберите Module SDK, затем Next.
*Шаг 2.* Group — com.itheima, Artifact — leyou, Name — ZuulServer, Description — Zuul For leyou Project, затем Next.
*Шаг 3.* Выберите Spring Cloud Discovery, затем Eureka Discovery Client, затем Next.
*Шаг 4.* Module name — ZuulServer, Content root — путь + ZuulServer. Выберите Finish. В папке проекта будет создана папка ZuulServer.
**4.7.2. Конфигурация pom.xml**
В созданном проекте откройте pom.xml и настройте зависимости. spring-cloud-starter-netflix-eureka-client — это jar-пакет, который включает клиент Eureka в проект. spring-boot-starter-web — это jar-пакеты, которые используются для разработки веб-модулей в среде web.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouZuul</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.7.3. Конфигурация файла application.properties
Откройте файл src\man\resources\application.properties в созданном проекте и настройте порт и другие параметры.
server.port=80
spring.application.name=leyou-zuul
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
#忽略框架默认的服务映射路径
zuul.ignored-services='*'
#不忽略框架与权限相关的头信息
zuul.ignore-security-headers=false
zuul.host.socket-timeout-millis=60000
zuul.host.connect-timeout-millis=60000
zuul.host.max-total-connections=500
zuul.routes.leyou-client.path=/leyouClient/**
zuul.routes.leyou-client.serviceId=leyou-client
#防止session不一致问题
zuul.routes.leyou-client.sensitiveHeaders="*"
zuul.routes.leyou-order.path=/leyouOrder/**
zuul.routes.leyou-order.serviceId=leyou-order
zuul.routes.leyou-order.sensitiveHeaders="*"
zuul.routes.leyou-user.path=/leyouUser/**
zuul.routes.leyou-user.serviceId=leyou-user
zuul.routes.leyou-user.sensitiveHeaders="*"
zuul.routes.leyou-stock.path=/leyouStock/**
zuul.routes.leyou-stock.serviceId=leyou-stock
zuul.routes.leyou-stock.sensitiveHeaders="*"
zuul.routes.leyou-storage.path=/leyouStorage/**
zuul.routes.leyou-storage.serviceId=leyou-storage
zuul.routes.leyou-storage.sensitiveHeaders="*"
zuul.routes.leyou-time-server.path=/leyouTimeServer/**
zuul.routes.leyou-time-server.serviceId=leyou-time-server
zuul.routes.leyou-time-server.sensitiveHeaders="*"
Обратите внимание, что zuul использует порт 80, поэтому при доступе к нему не нужно указывать номер порта.
В файле src\main\java\com.itheima.leyou\leyouTimeServerApplication добавьте код в уже существующий класс запуска. Текст запроса:
public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); }
**Перевод текста на русский язык:**
Паблик статик воид мэйн (стрэнг[] аргс) {
Спринг аппликейшн точка рун (КлиентАппликатион точка класс, аргс);
}
``` **Внимание: в application.properties в spring.cloud.config.server.native.search-locations указывается путь к локальному файлу, который указывает на shared, в большинстве программных компаний этот элемент может находиться на каком-либо сервере или в удалённом репозитории.**
**Файлы в папке share имеют следующие правила именования: имя файла конфигурации для каждого сервиса формируется из spring.application.name в каждом конфигурационном файле сервиса и значения spring.cloud.config.profile.**
Например: leyou-time-server — это имя spring.application.name для времени обслуживания (leyouTimeServer), а значение spring.cloud.config.profile в файле конфигурации приложения — dev, поэтому файл конфигурации для товарного сервиса называется leyou-time-server-dev.properties.
Содержание файла leyou-time-server-dev.properties:
```properties
server.port=8000
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
В созданном проекте откройте src\main\java\com.itheima.leyou\leyouConfigApplication. В уже созданном классе запуска добавьте @EnableEurekaClient для запуска Eureka Client и @EnableConfigServer для запуска Config Configuration Service.
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
На примере товарного сервиса:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
spring.application.name=leyou-time-server
spring.cloud.config.profile=dev
spring.cloud.config.uri=http://localhost:2000
spring.cloud.config.label=master
spring.profiles.active=dev
Запустите Config Server и товарный сервис. На Eureka Registration Center вы увидите leyou-config-server и leyou-stock сервисы, что означает успешную настройку сервисов.
Измените файл конфигурации времени обслуживания (leyouTimeServer) application.properties, изменив порт на 8001:
server.port=8001
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
Запустите Config Server, затем время обслуживания. Вы увидите, что запускается порт 8000.
Не запуская Config Server, запустите время обслуживания. Вы увидите, что запустится порт 8001.
Вывод: при запуске сервиса сначала используется bootstrap.properties, если его нет, то используется application.properties. Это доказывает, что bootstrap имеет более высокий приоритет, чем application.
Адрес: http://localhost/leyouClient/page/limitPolicyPage.html
Выберите товар, введите цену продажи, количество товаров в продаже, дату начала и окончания продаж, нажмите «Сохранить».
Адрес: http://localhost/leyouClient/page/loginPage.html
Войдите на страницу входа, введите номер телефона и пароль, перейдите на страницу списка товаров.
Адрес: http://localhost/leyouClient/page/stockListPage.html
Перейдите на страницу списка товаров и выберите товар.
Адрес: http://localhost/leyouClient/page/stockDetailPage.html?sku_id=27359021557
На странице товара нажмите «Купить сейчас».
Адрес: http://localhost/leyouClient/page/createOrderPage.html?sku_id=27359021557
На странице отправки заказа нажмите «Отправить заказ».
Адрес: http://localhost/leyouClient/page/payPage.html?order_id=1565061554849
Нажмите «Оплата через WeChat», появится сообщение «Платёж выполнен успешно».
О очереди:
Ответ: проекты торгового центра, система продаж торгового центра. Система продаж торгового центра является ключевым маркетинговым мероприятием торгового центра, поэтому она была выделена в отдельную систему. Я был главным разработчиком системы продаж.
Ответ: проект уже запущен, но мы являемся аутсорсинговой компанией, и после завершения разработки проекта мы не знаем, где он будет развёрнут, поэтому мы не можем получить к нему доступ.
Ответ: поскольку я разработчик, доход от этого проекта контролируется отделом продуктов, поэтому я не очень хорошо понимаю, как распределяется доход.
Ответ: в команде 10 человек, один дизайнер, шесть разработчиков Java, один фронтенд-разработчик, два тестировщика. Я отвечаю за общую архитектуру и часть кода в группе из шести разработчиков. Обычно дизайнер пишет общий план проектирования, включая требования к исследованию, описание проекта, рабочий процесс, используемую технологию архитектуры и диаграмму архитектуры. Мы разрабатываем более подробный дизайн, включая структуру таблицы, бизнес-процессы, которые может обрабатывать каждый микросервис, и даже псевдокод. Фронтенд отвечает за разработку страницы, мы предоставляем версию для тестирования, а затем передаём её тестировщику.
Ответ:
Техническая архитектура: изначально мы использовали базу данных для управления перепродажей и дефицитом товаров, но это вызвало серьёзные проблемы с производительностью во время стресс-тестирования, поскольку база данных вызвала множество взаимоблокировок, и передняя страница перестала отвечать. Позже мы использовали технологию кэширования Redis для решения проблемы перепродажи и дефицита товаров, помещая все товары и запасы, участвующие в распродаже, в кэш Redis, а затем уменьшая количество запасов при каждой продаже, обрабатывая всё в кэше, эффективно решая проблему производительности.
Разработка: когда я разрабатывал код для уменьшения запасов, запасы не могли быть уменьшены должным образом, поэтому я сначала проверил журнал консоли, чтобы увидеть, есть ли сообщения об ошибках, проанализировал их, а затем установил точки останова, чтобы отследить код построчно и найти проблему.
Взаимодействие с фронтендом: данные, полученные фронтендом от нашего интерфейса, не могут быть проанализированы, поэтому нам необходимо вывести данные, возвращаемые нашим интерфейсом, чтобы проверить, есть ли искажённые символы, неправильный формат или неправильные поля, а также необходимо совместно отладить с фронтендом.
Тестирование: после того как тестировщик предоставит отчёт о тестировании, некоторые проблемы трудно воспроизвести, сначала необходимо использовать их данные для имитации, посмотреть, является ли это проблемой данных, затем использовать их среду для отслеживания кода, чтобы найти проблему.
Ответ: распределённые транзакции обрабатываются с помощью TCC. Режим обработки: пример с заказами и запасами
Обработка происходит в режиме, который включает в себя заказы и запасы. Логика try для сервисов заказов и запасов выполняется после открытия транзакции. У всех всё работает нормально: сервис заказов выполняет бизнес-код, затем логику confirm, запасы также выполняют бизнес-код и логику confirm. Все вместе отправляют данные. Если логика try для сервиса запасов не срабатывает, сервис заказов получает уведомление о выполнении логики cancel.
В случае, когда ни у кого нет проблем, требуется провести одно взаимодействие через try и ещё одно через confirm. Поэтому координация между двумя сервисами имеет свою стоимость. Когда объём невелик, это не проблема, но когда объём становится большим, возникает эффект очереди.
Поэтому мы не используем распределённую обработку транзакций между несколькими микросервисами. Сначала мы обрабатываем запросы через Redis, а затем разделяем бизнес-процессы. Для решения задач мы используем промежуточное ПО — очередь сообщений RabbitMQ. Каждый сервис открывает собственную обработку транзакций, что позволяет более эффективно решать проблемы параллелизма.
6.7. Сколько времени заняло разработка проекта?
Ответ: около 10 дней ушло на предварительное исследование и общее проектирование. На подробное проектирование ушло примерно 5 дней. Разработка заняла около 20 дней, плюс тестирование, отладка, исправление ошибок, повторное тестирование и запуск должны занять примерно полтора месяца.
6.8. Какие технологии использовались в проекте?
Ответ: используется архитектура микросервисов SpringCloud для решения проблемы перегрузки бэкэнда. В соответствии с различными модулями происходит разделение, каждая команда разработчиков отвечает за один микросервис. Используется промежуточное ПО Redis для кэширования и RabbitMQ для разделения бизнес-процессов.
6.9. Данные в проекте извлекаются только через SQL-запросы?
Ответ: не совсем. Например, политика мгновенных распродаж товаров извлекается из Redis, а не через SQL-запрос. Список товаров и их детали извлекаются через SQL-запросы. Количество товаров, участвующих в распродаже, контролируется через Redis, а не с помощью SQL-запросов в реальном времени.
6.10. Какой модуль в проекте был самым сложным?
Ответ: обработка бизнес-логики модуля запасов была самой сложной. Запасы — это чувствительные данные, и все операции должны выполняться очень осторожно. Ошибки в запасах могут привести к значительным потерям для бизнеса.
Основная сложность этого проекта — как справиться с большими объёмами параллельных запросов. Мы контролируем перепродажу и дефицит через Redis и разделяем бизнес-операции через очередь сообщений RabbitMQ.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )