diff --git a/README.md b/README.md index dad8ab0..fdce005 100644 --- a/README.md +++ b/README.md @@ -1 +1,133 @@ -# Server.Java \ No newline at end of file +# Java сервер для OwnRadio + +Адрес сервера api.ownradio.ru + +Этот проект разрабатывается с целью тестирования и обкатки Java технологий + +Run +--- +##### Запускаем с помощью maven +* $ mvn -Dupload.dir=c:\ -Dspring.profiles.active=dev -Dserver.port=8080 spring-boot:run +* $ mvn -Dupload.dir=c:\ -Dspring.profiles.active=prod -Dserver.port=8080 spring-boot:run + +##### Запускаем упакованный jar +* $ java -Dupload.dir=c:\ -jar ownradio.jar --spring.profiles.active=dev --server.port=8080 +* $ java -Dupload.dir=c:\ -jar ownradio.jar --spring.profiles.active=prod --server.port=8080 + +Web API v3 +--- + +### Загрузка трека на сервер + +##### POST /v3/tracks +* `fileGuid` – UUID трека +* `filePath` - Полный локальный путь к файлу на пользовательском устройстве, включая имя файла (String) +* `deviceId` – UUID device +* `musicFile` – прикрепленный файл + +##### HttpStatus +* `400, "Bad Request"` - Если пользователь ввел некорректные данные +* `201, "Created"` – если все ок +* `500, "Internal Server Error"` – если произошел сбой на сервере + +### Получение трека с сервера +##### GET /v3/tracks/{trackId} +* `{trackId}` – UUID трека + +##### HttpStatus +* `200, "OK"` – в теле ответа будет лежать трек +* `404, "Not Found"` – если трек с таким recid не найден + +### Получение следующего трека с сервера +##### GET /v3/tracks/{deviceId}/next +* `{deviceId}` – UUID девайса + +##### Response +Content-Type →application/json;charset=UTF-8 +{ + "artist": "Artist", + "length": "duration", + "name": "Title", + "methodid": "1", + "id": "00000000-0000-0000-0000-000000000000" +} + +##### HttpStatus +* `200, "OK"` – в теле ответа будет лежать UUID трека +* `404, "Not Found"` – если трек с таким recid не найден + +### Сохранение истории треков +##### POST /v3/histories/{deviceId}/{trackId} +* `{trackId}` – UUID прослушанного трека +* `{deviceId}` – UUID устройства где был прослушан трек +* `lastListen` - Время последнего прослушивания или пропуска трека для данного пользователя ("yyyy-MM-ddTHH:mm:ss") +* `isListen` - Признак прослушан ли трек до конца: 1 - прослушан, -1 – нет (int) +* `methodid` - ID метода выбора трека (int), равен значению, полученному при получении данных для следующего трека + +##### HttpStatus +* `200, "OK"` – если все ок +* `500, "Internal Server Error"` – если произошел сбой на сервере + + +Web API v4 +--- +### Загрузка трека на сервер + +##### POST /v4/tracks +* `fileGuid` – UUID трека +* `filePath` - Полный локальный путь к файлу на пользовательском устройстве, включая имя файла (String) +* `deviceId` – UUID device +* `musicFile` – прикрепленный файл + +##### HttpStatus +* `400, "Bad Request"` - Если пользователь ввел некорректные данные +* `201, "Created"` – если все ок +* `500, "Internal Server Error"` – если произошел сбой на сервере + + +### Получение трека с сервера +##### GET /v4/tracks/{trackId} +* `{trackId}` – UUID трека + +##### HttpStatus +* `200, "OK"` – в теле ответа будет лежать трек +* `404, "Not Found"` – если трек с таким recid не найден + + +### Регистрация нового устройства +##### GET /v4/devices/{deviceId}/{deviceName}/registerdevice +* `{deviceId}` - UUID устройства +* `{deviceName}` - название устройства (может отсутствовать) +##### HttpStatus +* `200, "OK"` – если все ок +* `400, "Bad Request"` - Если пользователь ввел некорректные данные + + +### Получение следующего трека с сервера +##### GET /v4/tracks/{deviceId}/next +* `{deviceId}` – UUID девайса + +##### Response +Content-Type →application/json;charset=UTF-8 +{ + "artist": "Artist", + "length": "duration", + "name": "Title", + "id": "00000000-0000-0000-0000-000000000000", + "pathupload":"C:\\music\\track.mp3", + "timeexecute": "00:00:00.006238" +} + +### Сохранение истории треков +##### POST /v4/histories/{deviceId}/{trackId} +* `{historyId}` - UUID записи истории в БД устройства +* `{trackId}` – UUID прослушанного трека +* `{deviceId}` – UUID устройства где был прослушан трек +* `lastListen` - Время последнего прослушивания или пропуска трека для данного пользователя ("yyyy-MM-ddTHH:mm:ss") +* `isListen` - Признак прослушан ли трек до конца: 1 - прослушан, -1 – нет (int) + +##### HttpStatus +* `200, "OK"` – если все ок +* `208, "Already Reported"` - если история с таким UUID уже отдавалась +* `404, "Not found"` - если deviceId или trackid не найден +* `500, "Internal Server Error"` – если произошел сбой на сервере diff --git a/dbScripts/ownradio_db_v3.sql b/dbScripts/ownradio_db_v3.sql new file mode 100644 index 0000000..eb19fd0 --- /dev/null +++ b/dbScripts/ownradio_db_v3.sql @@ -0,0 +1,1332 @@ +-- Database: "ownRadioJava" + +-- DROP DATABASE "ownRadioJava"; + +CREATE DATABASE "ownRadioJava" + WITH OWNER = postgres + ENCODING = 'UTF8' + TABLESPACE = pg_default + LC_COLLATE = 'ru_RU.UTF-8' + LC_CTYPE = 'ru_RU.UTF-8' + CONNECTION LIMIT = -1; + + +-- Table: public.devices + +-- DROP TABLE public.devices; + +CREATE TABLE public.devices +( + recid uuid NOT NULL, + reccreated timestamp without time zone, + recname character varying(255), + recupdated timestamp without time zone, + userid uuid, + CONSTRAINT devices_pkey PRIMARY KEY (recid), + CONSTRAINT fk9xjj6x9ueb7id644i4ycukpug FOREIGN KEY (userid) + REFERENCES public.users (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkrfbri1ymrwywdydc4dgywe1bt FOREIGN KEY (userid) + REFERENCES public.users (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH ( + OIDS=FALSE, + autovacuum_enabled=true +); +ALTER TABLE public.devices + OWNER TO postgres; + + +-- Table: public.downloadtracks + +-- DROP TABLE public.downloadtracks; + +CREATE TABLE public.downloadtracks +( + recid uuid NOT NULL, + reccreated timestamp without time zone, + recname character varying(255), + recupdated timestamp without time zone, + deviceid uuid, + trackid uuid, + CONSTRAINT download_tracks_pkey PRIMARY KEY (recid), + CONSTRAINT fkcsqwol33buwhcijea4w2ty5k2 FOREIGN KEY (trackid) + REFERENCES public.tracks (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkerttthmbxldbrlonworltybaq FOREIGN KEY (deviceid) + REFERENCES public.devices (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkf38s60by3ys41nkrvo0wpghqu FOREIGN KEY (deviceid) + REFERENCES public.devices (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fklkvcfwa2nxrdfs6q20x3slfsk FOREIGN KEY (trackid) + REFERENCES public.tracks (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH ( + OIDS=FALSE, + autovacuum_enabled=true +); +ALTER TABLE public.downloadtracks + OWNER TO postgres; + + + +-- Table: public.histories + +-- DROP TABLE public.histories; + +CREATE TABLE public.histories +( + recid uuid NOT NULL, + reccreated timestamp without time zone, + recname character varying(255), + recupdated timestamp without time zone, + islisten integer NOT NULL, + lastlisten timestamp without time zone NOT NULL, + method character varying(255), + deviceid uuid, + trackid uuid, + userid uuid, + methodid integer, + CONSTRAINT histories_pkey PRIMARY KEY (recid), + CONSTRAINT fk66xoney4xhu7rp7yxwye0tuw4 FOREIGN KEY (deviceid) + REFERENCES public.devices (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fk6kk9amb55jghcg30cxstw4yw FOREIGN KEY (trackid) + REFERENCES public.tracks (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fk8w9eva74w7t7xtf2opb33f8bq FOREIGN KEY (userid) + REFERENCES public.users (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkbc0htpqvevq196g2vpa9ipkci FOREIGN KEY (trackid) + REFERENCES public.tracks (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkbjn2i4ry8qwwp12wwbq4n96aa FOREIGN KEY (deviceid) + REFERENCES public.devices (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH ( + OIDS=FALSE, + autovacuum_enabled=true +); +ALTER TABLE public.histories + OWNER TO postgres; + + +-- Table: public.ratings + +-- DROP TABLE public.ratings; + +CREATE TABLE public.ratings +( + recid uuid NOT NULL, + reccreated timestamp without time zone, + recname character varying(255), + recupdated timestamp without time zone, + lastlisten timestamp without time zone NOT NULL, + ratingsum integer NOT NULL, + trackid uuid, + userid uuid, + CONSTRAINT ratings_pkey PRIMARY KEY (recid), + CONSTRAINT fk1wogw2je0eguqyvbegwgqwmku FOREIGN KEY (trackid) + REFERENCES public.tracks (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fk9obht0874ty4owpd9a3hqa7gr FOREIGN KEY (userid) + REFERENCES public.users (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkb3354ee2xxvdrbyq9f42jdayd FOREIGN KEY (userid) + REFERENCES public.users (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkkewx1qhpt2egcdq7x92cv63p7 FOREIGN KEY (trackid) + REFERENCES public.tracks (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH ( + OIDS=FALSE, + autovacuum_enabled=true +); +ALTER TABLE public.ratings + OWNER TO postgres; + +-- Index: public.idx_lastlisten + +-- DROP INDEX public.idx_lastlisten; + +CREATE INDEX idx_lastlisten + ON public.ratings + USING btree + (lastlisten); + +-- Index: public.idx_trackid + +-- DROP INDEX public.idx_trackid; + +CREATE INDEX idx_trackid + ON public.ratings + USING btree + (trackid); + +-- Index: public.idx_userid + +-- DROP INDEX public.idx_userid; + +CREATE INDEX idx_userid + ON public.ratings + USING btree + (userid); + + +-- Table: public.tracks + +-- DROP TABLE public.tracks; + +CREATE TABLE public.tracks +( + recid uuid NOT NULL, + reccreated timestamp without time zone, + recname character varying(255), + recupdated timestamp without time zone, + localdevicepathupload character varying(255) NOT NULL, + path character varying(255), + deviceid uuid, + uploaduserid uuid, + artist character varying(255), + iscensorial integer, + iscorrect integer, + isfilledinfo integer, + length integer, + size integer, + CONSTRAINT tracks_pkey PRIMARY KEY (recid), + CONSTRAINT fk4n44h9fs1to11otqj5ek7xtus FOREIGN KEY (deviceid) + REFERENCES public.devices (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fk7901v2785f03qrr9ruiwy7nd FOREIGN KEY (deviceid) + REFERENCES public.devices (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT fkfp7ki0smfcrvbvfjdnddxi1fb FOREIGN KEY (uploaduserid) + REFERENCES public.users (recid) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH ( + OIDS=FALSE, + autovacuum_enabled=true +); +ALTER TABLE public.tracks + OWNER TO postgres; + + +-- Table: public.users + +-- DROP TABLE public.users; + +CREATE TABLE public.users +( + recid uuid NOT NULL, + reccreated timestamp without time zone, + recname character varying(255), + recupdated timestamp without time zone, + CONSTRAINT users_pkey PRIMARY KEY (recid) +) +WITH ( + OIDS=FALSE, + autovacuum_enabled=true +); +ALTER TABLE public.users + OWNER TO postgres; + +-- Table: public.ratios + +-- DROP TABLE public.ratios; + +CREATE TABLE public.ratios +( + recid uuid NOT NULL DEFAULT uuid_generate_v4(), + userid1 uuid NOT NULL, + userid2 uuid NOT NULL, + ratio integer, + CONSTRAINT ratios_pkey PRIMARY KEY (recid) +) +WITH ( + OIDS=FALSE +); +ALTER TABLE public.ratios + OWNER TO postgres; + +-- Function: public.getnexttrack(uuid) + +-- DROP FUNCTION public.getnexttrack(uuid); + +CREATE OR REPLACE FUNCTION public.getnexttrack(IN i_deviceid uuid) + RETURNS TABLE(track character varying, methodid integer) AS +$BODY$ +DECLARE + i_userid uuid = i_deviceid; -- в дальнейшем заменить получением userid по deviceid +BEGIN + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + 'New user recname', + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_userid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + 'New device recname', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + + -- Возвращаем trackid, конвертируя его в character varying и methodid + RETURN QUERY SELECT + CAST((nexttrack.track) AS CHARACTER VARYING), + nexttrack.methodid + FROM getnexttrackid_v7(i_deviceid) AS nexttrack; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.getnexttrack(uuid) + OWNER TO "postgres"; + + +-- Function: public.getnexttrackid(uuid) + +-- DROP FUNCTION public.getnexttrackid(uuid); + +CREATE OR REPLACE FUNCTION public.getnexttrackid(i_deviceid uuid) + RETURNS SETOF uuid AS +$BODY$ +DECLARE + i_userid uuid = i_deviceid; + BEGIN + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + 'New user recname', + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_userid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + 'New device recname', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + + RETURN QUERY + SELECT tracks.recid + FROM tracks + LEFT JOIN + ratings + ON tracks.recid = ratings.trackid AND ratings.userid = i_userid + WHERE ratings.ratingsum >=0 OR ratings.ratingsum is null + ORDER BY RANDOM() + LIMIT 1; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.getnexttrackid(uuid) + OWNER TO postgres; + + +-- Function: public.getnexttrackid_string(uuid) + +-- DROP FUNCTION public.getnexttrackid_string(uuid); + +CREATE OR REPLACE FUNCTION public.getnexttrackid_string(i_deviceid uuid) + RETURNS SETOF character varying AS +$BODY$ +BEGIN + RETURN QUERY SELECT CAST(getnexttrackid(i_deviceid) AS CHARACTER VARYING); +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.getnexttrackid_string(uuid) + OWNER TO postgres; + + +-- Function: public.getnexttrackid_v2(uuid) + +-- DROP FUNCTION public.getnexttrackid_v2(uuid); + +CREATE OR REPLACE FUNCTION public.getnexttrackid_v2(IN i_deviceid uuid) + RETURNS TABLE(track uuid, methodid integer) AS +$BODY$ +DECLARE + i_userid uuid = i_deviceid; + rnd integer = (select trunc(random() * 10)); -- получаем случайное число от 0 до 9 + o_methodid integer; -- id метода выбора трека +BEGIN + + -- Выбираем следующий трек + + -- В 9/10 случаях выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + IF (rnd > 1) + THEN + o_methodid = 2; + RETURN QUERY + SELECT trackid, o_methodid + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - interval '1 day' + AND ratingsum >= 0 + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + END IF; + + -- В 1/10 случае выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; + RETURN QUERY + SELECT recid, o_methodid + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; + RETURN QUERY + SELECT recid, o_methodid + FROM tracks + ORDER BY RANDOM() + LIMIT 1; + RETURN; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.getnexttrackid_v2(uuid) + OWNER TO postgres; + + +-- Function: public.getnexttrackid_v3(uuid) + +-- DROP FUNCTION public.getnexttrackid_v3(uuid); + +CREATE OR REPLACE FUNCTION public.getnexttrackid_v3(IN i_deviceid uuid) + RETURNS TABLE(track uuid, methodid integer) AS +$BODY$ +DECLARE + i_userid uuid = i_deviceid; + rnd integer = (select trunc(random() * 1001)); + o_methodid integer; -- id метода выбора трека + owntracks integer; -- количество "своих" треков пользователя (обрезаем на 900 шт) +BEGIN + -- Выбираем следующий трек + + -- Определяем количество "своих" треков пользователя, ограничивая его 900 + owntracks = (SELECT COUNT(*) FROM ( + SELECT * FROM ratings + WHERE userid = i_userid + AND ratingsum >=0 + LIMIT 900) AS count) ; + + -- Если rnd меньше количества "своих" треков, выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + + IF (rnd < owntracks) + THEN + o_methodid = 2; -- метод выбора из своих треков + RETURN QUERY + SELECT trackid, o_methodid + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - interval '1 day' + AND ratingsum >= 0 + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + END IF; + + -- В 1/10 случае выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; -- метод выбора из непрослушанных треков + RETURN QUERY + SELECT recid, o_methodid + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; -- метод выбора случайного трека + RETURN QUERY + SELECT recid, o_methodid + FROM tracks + ORDER BY RANDOM() + LIMIT 1; + RETURN; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.getnexttrackid_v3(uuid) + OWNER TO postgres; + + +-- Function: public.getnexttrackid_v5(uuid) + +-- DROP FUNCTION public.getnexttrackid_v5(uuid); + +CREATE OR REPLACE FUNCTION public.getnexttrackid_v5(IN i_deviceid uuid) + RETURNS TABLE(track uuid, methodid integer) AS +$BODY$ +DECLARE + i_userid UUID = i_deviceid; + rnd INTEGER = (SELECT trunc(random() * 1001)); + o_methodid INTEGER; -- id метода выбора трека + owntracks INTEGER; -- количество "своих" треков пользователя (обрезаем на 900 шт) +BEGIN + -- Выбираем следующий трек + + -- Определяем количество "своих" треков пользователя, ограничивая его 900 + owntracks = (SELECT COUNT(*) + FROM ( + SELECT * + FROM ratings + WHERE userid = i_userid + AND ratingsum >= 0 + LIMIT 900) AS count); + + -- Если rnd меньше количества "своих" треков, выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + + IF (rnd < owntracks) + THEN + o_methodid = 2; -- метод выбора из своих треков + RETURN QUERY + SELECT + trackid, + o_methodid + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - INTERVAL '1 day' + AND ratingsum >= 0 + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + END IF; + + -- Если rnd больше количества "своих" треков - выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; -- метод выбора из непрослушанных треков + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; -- метод выбора случайного трека + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + ORDER BY RANDOM() + LIMIT 1; + RETURN; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.getnexttrackid_v5(uuid) + OWNER TO postgres; + + + +-- Function: public.getnexttrackid_v6(uuid) + +-- DROP FUNCTION public.getnexttrackid_v6(uuid); + +CREATE OR REPLACE FUNCTION public.getnexttrackid_v6(IN i_deviceid uuid) + RETURNS TABLE(track uuid, methodid integer) AS +$BODY$ +DECLARE + i_userid UUID = i_deviceid; + rnd INTEGER = (SELECT trunc(random() * 1001)); + o_methodid INTEGER; -- id метода выбора трека + owntracks INTEGER; -- количество "своих" треков пользователя (обрезаем на 900 шт) + arrusers uuid ARRAY; -- массив пользователей для i_userid с неотрицательнымм коэффициентами схожести интересов +BEGIN + -- Выбираем следующий трек + + -- Определяем количество "своих" треков пользователя, ограничивая его 900 + owntracks = (SELECT COUNT(*) + FROM ( + SELECT * + FROM ratings + WHERE userid = i_userid + AND ratingsum >= 0 + LIMIT 900) AS count); + + -- Если rnd меньше количества "своих" треков, выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + + IF (rnd < owntracks) + THEN + o_methodid = 2; -- метод выбора из своих треков + RETURN QUERY + SELECT + trackid, + o_methodid + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - INTERVAL '1 day' + AND ratingsum >= 0 + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + END IF; + + -- Если rnd больше количества "своих" треков - рекомендуем трек из треков пользователя с наибольшим + -- коэффициентом схожести интересов и наибольшим рейтингом прослушивания + + -- Выберем всех пользователей с неотрицательным коэффициентом схожести интересов для i_userid + -- отсортировав по убыванию коэффициентов + arrusers = (SELECT ARRAY (SELECT CASE WHEN userid1 = i_userid THEN userid2 + WHEN userid2 = i_userid THEN userid1 + ELSE NULL + END + FROM ratios + WHERE userid1 = i_userid OR userid2 = i_userid + AND ratio >= 0 + ORDER BY ratio DESC + )); + -- Выбираем пользователя i, с которым у него максимальный коэффициент. Среди его треков ищем трек + -- с максимальным рейтингом прослушивания, за исключением уже прослушанных пользователем i_userid. + -- Если рекомендовать нечего - берем следующего пользователя с наибольшим коэффициентом из оставшихся. + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(arrusers)) LOOP + o_methodid = 4; -- метод выбора из рекомендованных треков + RETURN QUERY + SELECT + trackid, + o_methodid + FROM ratings + WHERE userid = arrusers[i] + AND ratingsum > 0 + AND trackid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE deviceid = i_deviceid + AND reccreated > localtimestamp - INTERVAL '1 day') + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + ORDER BY ratingsum DESC + LIMIT 1; + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + RETURN; + END IF; + END LOOP; + + -- При отсутствии рекомендаций, выдавать случайный трек из непрослушанных треков с неотрицательным + -- рейтингом среди пользователей со схожим вкусом. + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(arrusers)) LOOP + o_methodid = 5; -- метод выбора из непрослушанных треков с неотрицательным рейтингом среди пользователей со схожим вкусом + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE recid NOT IN (SELECT trackid FROM ratings WHERE userid = arrusers[i] AND ratingsum < 0) + AND recid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + RETURN; + END IF; + END LOOP; + + -- Если таких треков нет - выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; -- метод выбора из непрослушанных треков + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; -- метод выбора случайного трека + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + ORDER BY RANDOM() + LIMIT 1; + RETURN; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.getnexttrackid_v6(uuid) + OWNER TO postgres; + + +-- Function: public.registertrack(uuid, character varying, character varying, uuid) + +-- DROP FUNCTION public.registertrack(uuid, character varying, character varying, uuid); + +CREATE OR REPLACE FUNCTION public.registertrack( + i_trackid uuid, + i_localdevicepathupload character varying, + i_path character varying, + i_deviceid uuid) + RETURNS boolean AS +$BODY$ +DECLARE + i_userid UUID = i_deviceid; + i_historyid UUID; + i_ratingid UUID; +BEGIN + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + SELECT uuid_generate_v4() + INTO i_historyid; + SELECT uuid_generate_v4() + INTO i_ratingid; + + -- + -- Функция добавляет запись о треке в таблицу треков и делает сопутствующие записи в + -- таблицу статистики прослушивания и рейтингов. Если пользователя, загружающего трек + -- нет в базе, то он добавляется в таблицу пользователей. + -- + + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + 'New user recname', + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_userid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + 'New device recname', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + + -- Добавляем трек в базу данных + INSERT INTO tracks (recid, localdevicepathupload, path, deviceid, reccreated) + VALUES (i_trackid, i_localdevicepathupload, i_path, i_deviceid, now()); + + -- Добавляем запись о прослушивании трека в таблицу истории прослушивания + INSERT INTO histories (recid, deviceid, trackid, isListen, lastListen, methodid, reccreated) + VALUES (i_historyid, i_deviceid, i_trackid, 1, now(), 2, now()); + + -- Добавляем запись в таблицу рейтингов + INSERT INTO ratings (recid, userid, trackid, lastListen, ratingsum, reccreated) + VALUES (i_ratingid, i_userid, i_trackid, now(), 1, now()); + + RETURN TRUE; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100; +ALTER FUNCTION public.registertrack(uuid, character varying, character varying, uuid) + OWNER TO postgres; + + +-- Function: public.selectdownloadhistory(uuid) + +-- DROP FUNCTION public.selectdownloadhistory(uuid); + +CREATE OR REPLACE FUNCTION public.selectdownloadhistory(IN i_deviceid uuid) + RETURNS TABLE(recid uuid, reccreated timestamp without time zone, recname character varying, recupdated timestamp without time zone, deviceid uuid, trackid uuid, isstatisticback integer) AS +$BODY$ + BEGIN + -- Выводит список треков по которым не была отдана история прослушивания для данного устройства + RETURN QUERY SELECT * FROM downloadtracks + WHERE + downloadtracks.trackid NOT IN + (SELECT histories.trackid FROM histories WHERE histories.deviceid = i_deviceid) + AND downloadtracks.deviceid = i_deviceid; + + END; + $BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION public.selectdownloadhistory(uuid) + OWNER TO postgres; + +-- Function: public.calculateratios() + +-- DROP FUNCTION public.calculateratios(); + +CREATE OR REPLACE FUNCTION public.calculateratios() + RETURNS boolean AS +$BODY$ +-- Функция рассчитывает таблицу коэффициентов схожести интересов для пар пользователей +DECLARE + -- объявляем курсор и запрос для него + curs1 CURSOR FOR SELECT * FROM( + -- рассчитываем матрицу коэффициентов схожести интересов для каждой пары пользователей + SELECT r.userid as userid01, r2.userid as userid02, SUM(r.ratingsum * r2.ratingsum) as s + FROM ratings r + INNER JOIN ratings r2 ON r.trackid = r2.trackid + AND r.userid != r2.userid + GROUP BY r.userid, r2.userid + ) AS cursor1; + cuser1 uuid; + cuser2 uuid; + cratio integer; +BEGIN + DROP TABLE IF EXISTS temp_ratio; + CREATE TEMP TABLE temp_ratio(userid1 uuid, userid2 uuid, ratio integer); + + OPEN curs1; -- открываем курсор + LOOP -- в цикле проходим по строкам результата запроса курсора + FETCH curs1 INTO cuser1, cuser2, cratio; + + IF NOT FOUND THEN EXIT; -- если данных нет - выходим + END IF; + -- если для данной пары пользователей уже записан коэффициент - пропускаем, иначе - записываем во временную таблицу + IF NOT EXISTS (SELECT * FROM temp_ratio WHERE userid1 = cuser2 AND userid2 = cuser1) THEN + INSERT INTO temp_ratio(userid1, userid2, ratio) + VALUES (cuser1, cuser2, cratio); + END IF; + END LOOP; + CLOSE curs1; -- закрываем курсор + + -- обновляем имеющиеся коэффициенты в таблице ratios + UPDATE ratios SET ratio = temp_ratio.ratio FROM temp_ratio + WHERE (ratios.userid1 = temp_ratio.userid1 AND ratios.userid2 = temp_ratio.userid2) + OR (ratios.userid1 = temp_ratio.userid2 AND ratios.userid2 = temp_ratio.userid1); + + -- если в ratios меньше пар пользователей, чем во временной таблице - вставляем недостающие записи + IF (SELECT COUNT(*) FROM ratios) < (SELECT COUNT(*) FROM temp_ratio) THEN + INSERT INTO ratios (userid1, userid2, ratio) + (SELECT tr.userid1, tr.userid2, tr.ratio FROM temp_ratio AS tr + LEFT OUTER JOIN ratios AS rr ON tr.userid1 = rr.userid1 AND tr.userid2 = rr.userid2 + WHERE rr.userid1 IS NULL OR rr.userid2 IS NULL + ); + END IF; + RETURN TRUE; +END; + +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100; +ALTER FUNCTION public.calculateratios() + OWNER TO postgres; + + +-- Function: public.updateratios(uuid) + +-- DROP FUNCTION public.updateratios(uuid); + +CREATE OR REPLACE FUNCTION public.updateratios(i_userid uuid) + RETURNS boolean AS +$BODY$ + + -- Функция обновляет таблицу коэффициентов схожести интересов для выбранного пользователя +DECLARE + -- объявляем курсор и запрос для него + curs1 CURSOR FOR SELECT * FROM( + -- рассчитываем матрицу коэффициентов схожести интересов для каждой пары пользователей + SELECT r.userid as userid01, r2.userid as userid02, SUM(r.ratingsum * r2.ratingsum) as s + FROM ratings r + INNER JOIN ratings r2 ON r.trackid = r2.trackid + AND r.userid != r2.userid + AND (r.userid = i_userid OR r2.userid = i_userid) + GROUP BY r.userid, r2.userid + ) AS cursor1; + cuser1 uuid; + cuser2 uuid; + cratio integer; +BEGIN + DROP TABLE IF EXISTS temp_ratio; + CREATE TEMP TABLE temp_ratio(userid1 uuid, userid2 uuid, ratio integer); + + OPEN curs1; -- открываем курсор + LOOP -- в цикле проходим по строкам результата запроса курсора + FETCH curs1 INTO cuser1, cuser2, cratio; + + IF NOT FOUND THEN EXIT; -- если данных нет - выходим + END IF; + -- если для данной пары пользователей уже записан коэффициент - пропускаем, иначе - записываем во временную таблицу + IF NOT EXISTS (SELECT * FROM temp_ratio WHERE userid1 = cuser2 AND userid2 = cuser1) THEN + INSERT INTO temp_ratio(userid1, userid2, ratio) + VALUES (cuser1, cuser2, cratio); + END IF; + END LOOP; + CLOSE curs1; -- закрываем курсор + + -- обновляем имеющиеся коэффициенты в таблице ratios + UPDATE ratios SET ratio = temp_ratio.ratio FROM temp_ratio + WHERE (ratios.userid1 = temp_ratio.userid1 AND ratios.userid2 = temp_ratio.userid2) + OR (ratios.userid1 = temp_ratio.userid2 AND ratios.userid2 = temp_ratio.userid1); + + -- если в ratios меньше пар пользователей, чем во временной таблице - вставляем недостающие записи + IF (SELECT COUNT(*) FROM ratios WHERE userid1 = i_userid or userid2 = i_userid) < (SELECT COUNT(*) FROM temp_ratio) THEN + INSERT INTO ratios (userid1, userid2, ratio) + (SELECT tr.userid1, tr.userid2, tr.ratio FROM temp_ratio AS tr + LEFT OUTER JOIN ratios AS rr ON tr.userid1 = rr.userid1 AND tr.userid2 = rr.userid2 + WHERE rr.userid1 IS NULL OR rr.userid2 IS NULL + ); + END IF; + RETURN TRUE; +END; + +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100; +ALTER FUNCTION public.calculateratios() + OWNER TO postgres; + + + -- Function: getnexttrackid_v7(uuid) + +-- DROP FUNCTION getnexttrackid_v7(uuid); + +CREATE OR REPLACE FUNCTION getnexttrackid_v7(IN i_deviceid uuid) + RETURNS TABLE(track uuid, methodid integer) AS +$BODY$ + +-- Функция выдачи следующего трека пользователю +-- С учетом рекомендаций от других пользователей + +DECLARE + i_userid UUID = i_deviceid; + rnd INTEGER = (SELECT trunc(random() * 1001)); + o_methodid INTEGER; -- id метода выбора трека + owntracks INTEGER; -- количество "своих" треков пользователя (обрезаем на 900 шт) + arrusers uuid ARRAY; -- массив пользователей для i_userid с неотрицательнымм коэффициентами схожести интересов + exceptusers uuid ARRAY; -- массив пользователей для i_userid с котороми не было пересечений по трекам +BEGIN + -- Выбираем следующий трек + + -- Определяем количество "своих" треков пользователя, ограничивая его 900 + owntracks = (SELECT COUNT(*) + FROM ( + SELECT * + FROM ratings + WHERE userid = i_userid + AND ratingsum >= 0 + LIMIT 900) AS count); + + -- Если rnd меньше количества "своих" треков, выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + + IF (rnd < owntracks) + THEN + o_methodid = 2; -- метод выбора из своих треков + RETURN QUERY + SELECT + trackid, + o_methodid + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - INTERVAL '1 day' + AND ratingsum >= 0 + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + END IF; + + -- Если rnd больше количества "своих" треков - рекомендуем трек из треков пользователя с наибольшим + -- коэффициентом схожести интересов и наибольшим рейтингом прослушивания + + -- Выберем всех пользователей с неотрицательным коэффициентом схожести интересов для i_userid + -- отсортировав по убыванию коэффициентов + arrusers = (SELECT ARRAY (SELECT CASE WHEN userid1 = i_userid THEN userid2 + WHEN userid2 = i_userid THEN userid1 + ELSE NULL + END + FROM ratios + WHERE userid1 = i_userid OR userid2 = i_userid + AND ratio >= 0 + ORDER BY ratio DESC + )); + -- Выбираем пользователя i, с которым у него максимальный коэффициент. Среди его треков ищем трек + -- с максимальным рейтингом прослушивания, за исключением уже прослушанных пользователем i_userid. + -- Если рекомендовать нечего - берем следующего пользователя с наибольшим коэффициентом из оставшихся. + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(arrusers)) LOOP + o_methodid = 4; -- метод выбора из рекомендованных треков + RETURN QUERY + SELECT + trackid, + o_methodid + FROM ratings + WHERE userid = arrusers[i] + AND ratingsum > 0 + AND trackid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE deviceid = i_deviceid + AND reccreated > localtimestamp - INTERVAL '1 day') + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + ORDER BY ratingsum DESC + LIMIT 1; + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + RETURN; + END IF; + END LOOP; + + -- При отсутствии рекомендаций, выдавать случайный трек из непрослушанных треков с неотрицательным + -- рейтингом среди пользователей с которыми не было пересечений по трекам. + exceptusers = (SELECT ARRAY ( + SELECT * FROM ( + SELECT recid FROM users WHERE recid != i_userid + EXCEPT + (SELECT CASE WHEN userid1 = i_userid THEN userid2 + WHEN userid2 = i_userid THEN userid1 + ELSE NULL + END + FROM ratios WHERE userid1 = i_userid OR userid2 = i_userid) + ) AS us + ORDER BY RANDOM() + ) + ); + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(exceptusers)) LOOP + o_methodid = 6; -- метод выбора из непрослушанных треков с неотрицательным рейтингом среди пользователей с которыми не было пересечений + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE recid IN (SELECT trackid FROM ratings WHERE userid = exceptusers[i] AND ratingsum >= 0) + AND recid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + RETURN; + ELSE + + END IF; + END LOOP; + + -- Если таких треков нет - выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; -- метод выбора из непрослушанных треков + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '1 day') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; -- метод выбора случайного трека + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + ORDER BY RANDOM() + LIMIT 1; + RETURN; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 + ROWS 1000; +ALTER FUNCTION getnexttrackid_v7(uuid) + OWNER TO "postgres"; + + +-- Function: public.getrecommendedtrackid_v1(uuid) + +-- DROP FUNCTION public.getrecommendedtrackid_v1(uuid); + +CREATE OR REPLACE FUNCTION public.getrecommendedtrackid_v1(in_userid uuid) + RETURNS uuid AS +$BODY$ + +DECLARE +preferenced_track uuid; + +BEGIN + -- Соединяем таблицу tracks с таблицой сумм произведений рейтинга трека на коэффициент + -- у конкретного пользователя для возможности вывода дополнительной информации о треке + -- в отладочных целях и для фильтра по столбцам tracks + SELECT tracks.recid INTO preferenced_track + --tracks.recid, table2.sum_rate, tracks.localdevicepathupload, tracks.path + FROM tracks + INNER JOIN ( + --Группируем по треку и считаем сумму произведений рейтингов на коэффициент для + --каждого из них + SELECT trackid, SUM(track_rating) AS sum_rate + FROM( + --Запрашиваем таблицу с рейтингом всех треков, оцененных пользователями, которые имеют коэффициент + --с исходным, умноженным на их коэффициент + SELECT ratings.trackid, ratings.ratingsum * ratios.ratio AS track_rating, ratings.userid, ratios.ratio + FROM ratings + INNER JOIN ratios + --Выбираем рейтинги треков у тех пользователей, у которых есть пересечение + --с исходным в таблице ratios (кэффициенты совпадения вкусов), проверяя сначала + --с левой стороны + ON ((ratings.userid = ratios.userid2 AND ratios.userid1 = in_userid) + -- потом с правой + OR (ratings.userid = ratios.userid1 AND ratios.userid2 = in_userid)) + AND ratings.userid <> in_userid --Выбирем все оценки треков, кроме оценок, данных исходным пользователем + AND ratios.ratio > 0 --Считать рейтинги треков, только у пользователей с положительным коэффициентом совпадения вкусов с исходным + ) AS TracksRatings + GROUP BY trackid + ORDER BY sum_rate DESC + ) AS table2 + ON tracks.recid = table2.trackid + AND tracks.isexist = 1 --Трек должен существовать на сервере + AND tracks.iscensorial <> 0 --Трек не должен быть помечен как нецензурный + AND tracks.length >= 120 + --Трек не должен был выдаваться исходному пользователю в течении последних двух месяцев + AND tracks.recid NOT IN (SELECT trackid FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL '2 months' AND deviceid = in_userid) + AND sum_rate > 0 --В итоге рекомендоваться будут только треки с положительной суммой произведений рейтингов на коэффициенты + ORDER BY table2.sum_rate DESC + --Сортировка по второму столбцу нужна для случаев, когда получаем много треков с одинковым table2.sum_rate, + --в таких случаях план выполнения запроса меняется и производительность сильно падает + ,tracks.recid + LIMIT 1; + RETURN preferenced_track; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100; +ALTER FUNCTION public.getrecommendedtrackid_v1(uuid) + OWNER TO postgres; + diff --git a/src/README.md b/src/README.md index b499500..4f3a4cd 100644 --- a/src/README.md +++ b/src/README.md @@ -36,7 +36,7 @@ Web API ##### HttpStatus * `200, "OK"` – в теле ответа будет лежать трек -* `404, "Not Found"` – если трек с таким id не найден +* `404, "Not Found"` – если трек с таким recid не найден ### Получение следующего трека с сервера ##### GET /api/v2/tracks/{deviceId}/next @@ -44,7 +44,7 @@ Web API ##### HttpStatus * `200, "OK"` – в теле ответа будет лежать UUID трека -* `404, "Not Found"` – если трек с таким id не найден +* `404, "Not Found"` – если трек с таким recid не найден ### Сохранение истории треков ##### POST /api/v2/histories/{deviceId}/{trackId} diff --git a/src/src/main/java/ownradio/command/InitDb.java b/src/src/main/java/ownradio/command/InitDb.java index c9fb3fc..4568789 100644 --- a/src/src/main/java/ownradio/command/InitDb.java +++ b/src/src/main/java/ownradio/command/InitDb.java @@ -23,6 +23,6 @@ public class InitDb implements CommandLineRunner { @Override public void run(String... strings) throws Exception { User user = userService.save(new User()); - log.debug("User id: {}", user.getId()); + log.debug("User recid: {}", user.getRecid()); } } diff --git a/src/src/main/java/ownradio/domain/AbstractEntity.java b/src/src/main/java/ownradio/domain/AbstractEntity.java index fc5f772..d740f08 100644 --- a/src/src/main/java/ownradio/domain/AbstractEntity.java +++ b/src/src/main/java/ownradio/domain/AbstractEntity.java @@ -7,7 +7,6 @@ import javax.persistence.*; import java.io.Serializable; -import java.util.Calendar; import java.util.Date; import java.util.UUID; @@ -27,24 +26,21 @@ public abstract class AbstractEntity implements Serializable { @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid2") @Column(unique = true) - private UUID id; + private UUID recid; - private String name; + private String recname; - @Temporal(TemporalType.TIMESTAMP) - private Calendar createdAt; - - @Temporal(TemporalType.TIMESTAMP) - private Calendar updatedAt; + private Date reccreated; + private Date recupdated; @PrePersist public void beforePersist() { - setCreatedAt(Calendar.getInstance()); + setReccreated(new Date()); } @PreUpdate public void beforeUpdate() { - setUpdatedAt(Calendar.getInstance()); + setRecupdated(new Date()); } @Override @@ -54,12 +50,12 @@ public boolean equals(Object o) { AbstractEntity that = (AbstractEntity) o; - return id != null ? id.equals(that.id) : that.id == null; + return recid != null ? recid.equals(that.recid) : that.recid == null; } @Override public int hashCode() { - return id != null ? id.hashCode() : 0; + return recid != null ? recid.hashCode() : 0; } } diff --git a/src/src/main/java/ownradio/domain/Device.java b/src/src/main/java/ownradio/domain/Device.java index 66d54cb..e6910df 100644 --- a/src/src/main/java/ownradio/domain/Device.java +++ b/src/src/main/java/ownradio/domain/Device.java @@ -23,8 +23,12 @@ @Table(name = "devices") public class Device extends AbstractEntity { @ManyToOne - @JoinColumn(name = "user_id") + @JoinColumn(name = "userid") private User user; - private String name; +// private String name; + public Device (User user, String name){ + setRecname(name); + setUser(user); + } } diff --git a/src/src/main/java/ownradio/domain/DownloadTrack.java b/src/src/main/java/ownradio/domain/DownloadTrack.java index eaece5f..447832d 100644 --- a/src/src/main/java/ownradio/domain/DownloadTrack.java +++ b/src/src/main/java/ownradio/domain/DownloadTrack.java @@ -5,10 +5,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; -import javax.persistence.Entity; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; +import javax.persistence.*; /** * Сущность для хранения информации о скаченных треках @@ -20,14 +17,14 @@ @NoArgsConstructor @AllArgsConstructor @Entity -@Table(name = "download_tracks") +@Table(name = "downloadtracks") public class DownloadTrack extends AbstractEntity { @ManyToOne - @JoinColumn(name = "device_id") + @JoinColumn(name = "deviceid") private Device device; @ManyToOne - @JoinColumn(name = "track_id") + @JoinColumn(name = "trackid") private Track track; } diff --git a/src/src/main/java/ownradio/domain/History.java b/src/src/main/java/ownradio/domain/History.java index d62bd2e..c41c696 100644 --- a/src/src/main/java/ownradio/domain/History.java +++ b/src/src/main/java/ownradio/domain/History.java @@ -24,21 +24,20 @@ public class History extends AbstractEntity { @ManyToOne - @JoinColumn(name = "track_id") + @JoinColumn(name = "trackid") private Track track; - @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") - @Column(nullable = false) - @Temporal(TemporalType.TIMESTAMP) + @DateTimeFormat(pattern = "dd/MM/yyyy") + @Column(name = "lastlisten", nullable = false) private Date lastListen; - @Column(nullable = false) - private Integer isListen; // 1, -1 + @Column(name = "islisten", nullable = false) + private int isListen; // 1, -1 @Column(nullable = false) private String method; @ManyToOne - @JoinColumn(name = "device_id") + @JoinColumn(name = "deviceid") private Device device; } diff --git a/src/src/main/java/ownradio/domain/Rating.java b/src/src/main/java/ownradio/domain/Rating.java index c6600fc..78ab1b9 100644 --- a/src/src/main/java/ownradio/domain/Rating.java +++ b/src/src/main/java/ownradio/domain/Rating.java @@ -8,7 +8,6 @@ import org.springframework.format.annotation.DateTimeFormat; import javax.persistence.*; -import java.util.Calendar; import java.util.Date; /** @@ -24,18 +23,17 @@ @Table(name = "ratings") public class Rating extends AbstractEntity { @ManyToOne - @JoinColumn(name = "user_id") + @JoinColumn(name = "userid") private User user; @ManyToOne - @JoinColumn(name = "track_id") + @JoinColumn(name = "trackid") private Track track; - @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + @DateTimeFormat(pattern = "dd/MM/yyyy") @Column(nullable = false) - @Temporal(TemporalType.TIMESTAMP) - private Calendar lastListen; + private Date lastlisten; @Column(nullable = false) - private Integer ratingSum; + private Integer ratingsum; } diff --git a/src/src/main/java/ownradio/domain/Track.java b/src/src/main/java/ownradio/domain/Track.java index 2ea7e90..45f4829 100644 --- a/src/src/main/java/ownradio/domain/Track.java +++ b/src/src/main/java/ownradio/domain/Track.java @@ -23,10 +23,10 @@ public class Track extends AbstractEntity { private String path; @ManyToOne - @JoinColumn(name = "device_id") + @JoinColumn(name = "deviceid") private Device device; @Column(nullable = false) - private String localDevicePathUpload; + private String localdevicepathupload; } diff --git a/src/src/main/java/ownradio/domain/User.java b/src/src/main/java/ownradio/domain/User.java index 58bdc8b..1f1c8c9 100644 --- a/src/src/main/java/ownradio/domain/User.java +++ b/src/src/main/java/ownradio/domain/User.java @@ -19,6 +19,6 @@ @Table(name = "users") public class User extends AbstractEntity { public User(String name) { - setName(name); + setRecname(name); } } diff --git a/src/src/main/java/ownradio/repository/RatingRepository.java b/src/src/main/java/ownradio/repository/RatingRepository.java index b947905..b739402 100644 --- a/src/src/main/java/ownradio/repository/RatingRepository.java +++ b/src/src/main/java/ownradio/repository/RatingRepository.java @@ -1,8 +1,12 @@ package ownradio.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import ownradio.domain.Rating; +import ownradio.domain.Track; +import ownradio.domain.User; +import java.util.Date; import java.util.UUID; /** @@ -11,4 +15,8 @@ * @author Alpenov Tanat */ public interface RatingRepository extends JpaRepository { + + Rating findByUser(User user); + + Rating findByUserAndTrack(User user, Track track); } diff --git a/src/src/main/java/ownradio/service/impl/HistoryServiceImpl.java b/src/src/main/java/ownradio/service/impl/HistoryServiceImpl.java index d1e7785..cf40353 100644 --- a/src/src/main/java/ownradio/service/impl/HistoryServiceImpl.java +++ b/src/src/main/java/ownradio/service/impl/HistoryServiceImpl.java @@ -2,23 +2,44 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ownradio.domain.History; +import ownradio.domain.Rating; import ownradio.repository.HistoryRepository; +import ownradio.repository.RatingRepository; import ownradio.service.HistoryService; @Service public class HistoryServiceImpl implements HistoryService { private final HistoryRepository historyRepository; + private final RatingRepository ratingRepository; @Autowired - public HistoryServiceImpl(HistoryRepository historyRepository) { + public HistoryServiceImpl(HistoryRepository historyRepository, RatingRepository ratingRepository) { this.historyRepository = historyRepository; + this.ratingRepository = ratingRepository; } - + @Transactional @Override public void save(History history) { historyRepository.saveAndFlush(history); + + Rating rating = ratingRepository.findByUserAndTrack(history.getDevice().getUser(), history.getTrack()); + if(rating != null) { + int ratingsum = rating.getRatingsum() + history.getIsListen(); + rating.setLastlisten(history.getLastListen()); + rating.setRatingsum(ratingsum); + ratingRepository.saveAndFlush(rating); + } + else { + rating = new Rating(); + rating.setUser(history.getDevice().getUser()); + rating.setTrack(history.getTrack()); + rating.setLastlisten(history.getLastListen()); + rating.setRatingsum(history.getIsListen()); + ratingRepository.saveAndFlush(rating); + } } } diff --git a/src/src/main/java/ownradio/service/impl/TrackServiceImpl.java b/src/src/main/java/ownradio/service/impl/TrackServiceImpl.java index 76ae61f..5e4936d 100644 --- a/src/src/main/java/ownradio/service/impl/TrackServiceImpl.java +++ b/src/src/main/java/ownradio/service/impl/TrackServiceImpl.java @@ -29,7 +29,7 @@ public Track getById(UUID id) { } @Override - @Transactional(readOnly = true) + @Transactional public UUID getNextTrackId(UUID deviceId) { return trackRepository.getNextTrackId(deviceId); } @@ -37,15 +37,15 @@ public UUID getNextTrackId(UUID deviceId) { @Override @Transactional public void save(Track track, MultipartFile file) { - boolean result = trackRepository.registerTrack(track.getId(), track.getLocalDevicePathUpload(), track.getPath(), track.getDevice().getId()); + boolean result = trackRepository.registerTrack(track.getRecid(), track.getLocaldevicepathupload(), track.getPath(), track.getDevice().getRecid()); if (!result) { throw new RuntimeException(); } - Track storeTrack = trackRepository.findOne(track.getId()); + Track storeTrack = trackRepository.findOne(track.getRecid()); - String dirName = storeTrack.getDevice().getUser().getId().toString(); - String fileName = storeTrack.getId() + "." + StringUtils.getFilenameExtension(file.getOriginalFilename()); + String dirName = storeTrack.getDevice().getUser().getRecid().toString(); + String fileName = storeTrack.getRecid() + "." + StringUtils.getFilenameExtension(file.getOriginalFilename()); String filePath = ResourceUtil.save(dirName, fileName, file); storeTrack.setPath(filePath); diff --git a/src/src/main/java/ownradio/service/impl/UserServiceImpl.java b/src/src/main/java/ownradio/service/impl/UserServiceImpl.java index 09816ef..c5c88dc 100644 --- a/src/src/main/java/ownradio/service/impl/UserServiceImpl.java +++ b/src/src/main/java/ownradio/service/impl/UserServiceImpl.java @@ -2,6 +2,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ownradio.domain.User; import ownradio.repository.UserRepository; import ownradio.service.UserService; diff --git a/src/src/main/java/ownradio/web/rest/v2/TrackController.java b/src/src/main/java/ownradio/web/rest/v2/TrackController.java index 0664ac9..c37c9c6 100644 --- a/src/src/main/java/ownradio/web/rest/v2/TrackController.java +++ b/src/src/main/java/ownradio/web/rest/v2/TrackController.java @@ -42,14 +42,14 @@ private static class TrackDTO { public Track getTrack() { Device device = new Device(); - device.setId(deviceId); + device.setRecid(deviceId); Track track = new Track(); - track.setId(fileGuid); - track.setName(fileName); + track.setRecid(fileGuid); + track.setRecname(fileName); track.setDevice(device); track.setPath("---"); - track.setLocalDevicePathUpload(filePath); + track.setLocaldevicepathupload(filePath); return track; } diff --git a/src/src/main/resources/application-dev.properties b/src/src/main/resources/application-dev.properties index 4d4bbd9..5814481 100644 --- a/src/src/main/resources/application-dev.properties +++ b/src/src/main/resources/application-dev.properties @@ -9,7 +9,7 @@ spring.http.multipart.max-request-size=256Mb #spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=create +spring.jpa.hibernate.ddl-auto=update logging.file=logs/app.log logging.level.ownradio=debug logging.level.org.springframework=warn diff --git a/src/src/main/resources/application-prod.properties b/src/src/main/resources/application-prod.properties index ccfbce5..a6e49eb 100644 --- a/src/src/main/resources/application-prod.properties +++ b/src/src/main/resources/application-prod.properties @@ -12,7 +12,7 @@ spring.http.multipart.max-request-size=256Mb #spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=create +spring.jpa.hibernate.ddl-auto=update logging.file=logs/app.log logging.level.ownradio=info logging.level.org.springframework=warn diff --git a/src/src/main/resources/data/postgresql/schema.sql b/src/src/main/resources/data/postgresql/schema.sql index 328a09d..a9916e3 100644 --- a/src/src/main/resources/data/postgresql/schema.sql +++ b/src/src/main/resources/data/postgresql/schema.sql @@ -1,10 +1,43 @@ CREATE OR REPLACE FUNCTION getnexttrackid(IN i_deviceid UUID) RETURNS SETOF UUID AS ' -BEGIN +DECLARE + i_userid uuid = i_deviceid; + BEGIN + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + ''New user recname'', + now(); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + ''New device recname'', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + RETURN QUERY - SELECT id + SELECT tracks.recid FROM tracks + LEFT JOIN + ratings + ON tracks.recid = ratings.trackid AND ratings.userid = i_userid + WHERE ratings.ratingsum >=0 OR ratings.ratingsum is null ORDER BY RANDOM() LIMIT 1; END; @@ -30,14 +63,12 @@ CREATE OR REPLACE FUNCTION registertrack( RETURNS BOOLEAN AS ' DECLARE - i_userid UUID; + i_userid UUID = i_deviceid; i_historyid UUID; i_ratingid UUID; BEGIN CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; SELECT uuid_generate_v1() - INTO i_userid; - SELECT uuid_generate_v1() INTO i_historyid; SELECT uuid_generate_v1() INTO i_ratingid; @@ -50,41 +81,41 @@ BEGIN -- Добавляем устройство, если его еще не существует -- Если ID устройства еще нет в БД - IF NOT EXISTS(SELECT id + IF NOT EXISTS(SELECT recid FROM devices - WHERE id = i_deviceid) + WHERE recid = i_deviceid) THEN -- Добавляем нового пользователя - INSERT INTO users (id, name, created_at) SELECT + INSERT INTO users (recid, recname, reccreated) SELECT i_userid, - ''New user name'', + ''New user recname'', now(); -- Добавляем новое устройство - INSERT INTO devices (id, user_id, name, created_at) SELECT + INSERT INTO devices (recid, userid, recname, reccreated) SELECT i_deviceid, i_userid, - ''New device name'', + ''New device recname'', now(); ELSE - SELECT (SELECT user_id + SELECT (SELECT userid FROM devices - WHERE id = i_deviceid + WHERE recid = i_deviceid LIMIT 1) INTO i_userid; END IF; -- Добавляем трек в базу данных - INSERT INTO tracks (id, local_device_path_upload, path, device_id, created_at) + INSERT INTO tracks (recid, localdevicepathupload, path, deviceid, reccreated) VALUES (i_trackid, i_localdevicepathupload, i_path, i_deviceid, now()); -- Добавляем запись о прослушивании трека в таблицу истории прослушивания - INSERT INTO histories (id, device_id, track_id, is_listen, last_listen, method, created_at) + INSERT INTO histories (recid, deviceid, trackid, isListen, lastListen, method, reccreated) VALUES (i_historyid, i_deviceid, i_trackid, 1, now(), ''method'', now()); -- Добавляем запись в таблицу рейтингов - INSERT INTO ratings (id, user_id, track_id, last_listen, rating_sum, created_at) + INSERT INTO ratings (recid, userid, trackid, lastListen, ratingsum, reccreated) VALUES (i_ratingid, i_userid, i_trackid, now(), 1, now()); RETURN TRUE; diff --git a/src/src/test/java/ownradio/repository/HistoryRepositoryTest.java b/src/src/test/java/ownradio/repository/HistoryRepositoryTest.java index 4a6104c..6585d8d 100644 --- a/src/src/test/java/ownradio/repository/HistoryRepositoryTest.java +++ b/src/src/test/java/ownradio/repository/HistoryRepositoryTest.java @@ -12,7 +12,6 @@ import ownradio.domain.Track; import ownradio.domain.User; -import java.util.Calendar; import java.util.Date; import static org.hamcrest.CoreMatchers.nullValue; @@ -43,25 +42,25 @@ public void setUp() throws Exception { @Test public void createdAt() throws Exception { - assertThat(history.getCreatedAt(), not(nullValue())); - assertThat(history.getCreatedAt().getTime().toString(), is(Calendar.getInstance().getTime().toString())); + assertThat(history.getReccreated(), not(nullValue())); + assertThat(history.getReccreated().toString(), is(new Date().toString())); } @Test public void updatedAt() throws Exception { - assertThat(history.getCreatedAt(), not(nullValue())); - assertThat(history.getCreatedAt().getTime().toString(), is(Calendar.getInstance().getTime().toString())); - assertThat(history.getUpdatedAt(), is(nullValue())); + assertThat(history.getReccreated(), not(nullValue())); + assertThat(history.getReccreated().toString(), is(new Date().toString())); + assertThat(history.getRecupdated(), is(nullValue())); - History storeHistory = historyRepository.findOne(history.getId()); + History storeHistory = historyRepository.findOne(history.getRecid()); storeHistory.setIsListen(1); historyRepository.saveAndFlush(storeHistory); - assertThat(storeHistory.getCreatedAt(), not(nullValue())); - assertThat(storeHistory.getCreatedAt().getTime().toString(), is(history.getCreatedAt().getTime().toString())); + assertThat(storeHistory.getReccreated(), not(nullValue())); + assertThat(storeHistory.getReccreated().toString(), is(history.getReccreated().toString())); - assertThat(storeHistory.getUpdatedAt(), not(nullValue())); - assertThat(storeHistory.getUpdatedAt().getTime().toString(), is(Calendar.getInstance().getTime().toString())); + assertThat(storeHistory.getRecupdated(), not(nullValue())); + assertThat(storeHistory.getRecupdated().toString(), is(new Date().toString())); } diff --git a/src/src/test/java/ownradio/repository/RatingRepositoryTest.java b/src/src/test/java/ownradio/repository/RatingRepositoryTest.java new file mode 100644 index 0000000..3930678 --- /dev/null +++ b/src/src/test/java/ownradio/repository/RatingRepositoryTest.java @@ -0,0 +1,45 @@ +package ownradio.repository; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import ownradio.domain.Rating; +import ownradio.domain.Track; +import ownradio.domain.User; + +import java.util.UUID; + +import static org.junit.Assert.*; + +/** + * Created by a.polunina on 10.11.2016. + */ +@ActiveProfiles("prod") +@RunWith(SpringRunner.class) +@SpringBootTest +//@DataJpaTest +public class RatingRepositoryTest { + @Autowired + private RatingRepository ratingRepository; + @Test + public void findByUser() throws Exception { + User user = new User(); + user.setRecid(UUID.fromString("bfa8137b-c917-4496-8fe7-39202322d257")); + Rating rating = ratingRepository.findByUser(user); + System.out.println(rating); + } + + @Test + public void findByUserAndTrack() throws Exception { + User user = new User(); + Track track = new Track(); + user.setRecid(UUID.fromString("bfa8137b-c917-4496-8fe7-39202322d257")); + track.setRecid(UUID.fromString("bfa8137b-c917-4496-8fe7-39202322d257")); + Rating rating = ratingRepository.findByUserAndTrack(user, track); + System.out.println(rating); + } +} \ No newline at end of file diff --git a/src/src/test/java/ownradio/repository/TrackRepositoryTest.java b/src/src/test/java/ownradio/repository/TrackRepositoryTest.java index 42f2951..6c7618a 100644 --- a/src/src/test/java/ownradio/repository/TrackRepositoryTest.java +++ b/src/src/test/java/ownradio/repository/TrackRepositoryTest.java @@ -47,7 +47,7 @@ public void getNextTrackId() throws Exception { Set trackSet = new HashSet<>(); for (int i = 0; i < 3; i++) { - UUID track = trackRepository.getNextTrackId(device.getId()); + UUID track = trackRepository.getNextTrackId(device.getRecid()); trackSet.add(track); } diff --git a/src/src/test/java/ownradio/service/TrackServiceTest.java b/src/src/test/java/ownradio/service/TrackServiceTest.java index c3369cb..abe1f81 100644 --- a/src/src/test/java/ownradio/service/TrackServiceTest.java +++ b/src/src/test/java/ownradio/service/TrackServiceTest.java @@ -39,13 +39,13 @@ public class TrackServiceTest { public void setUp() throws Exception { trackService = new TrackServiceImpl(trackRepository); expected = new Track(); - expected.setId(trackId); + expected.setRecid(trackId); User user = new User(); - user.setId(userId); + user.setRecid(userId); Device device = new Device(user, "123"); - device.setId(deviceId); + device.setRecid(deviceId); expected.setDevice(device); } @@ -60,15 +60,15 @@ public void getNextTrackId() throws Exception { UUID actual = trackService.getNextTrackId(trackId); - assertThat(actual, equalTo(expected.getId())); + assertThat(actual, equalTo(expected.getRecid())); } @Test public void save() throws Exception { MockMultipartFile correctFile = new MockMultipartFile("file", "test.mp3", "text/plain", "Text".getBytes()); - given(this.trackRepository.registerTrack(expected.getId(), expected.getLocalDevicePathUpload(), expected.getPath(), expected.getDevice().getId())).willReturn(true); - given(this.trackRepository.findOne(expected.getId())).willReturn(expected); + given(this.trackRepository.registerTrack(expected.getRecid(), expected.getLocaldevicepathupload(), expected.getPath(), expected.getDevice().getRecid())).willReturn(true); + given(this.trackRepository.findOne(expected.getRecid())).willReturn(expected); trackService.save(expected, correctFile); assertThat(new File(expected.getPath()).exists(), is(true)); diff --git a/src/src/test/java/ownradio/web/rest/v2/HistoryControllerTest.java b/src/src/test/java/ownradio/web/rest/v2/HistoryControllerTest.java index 09b146d..b8b425c 100644 --- a/src/src/test/java/ownradio/web/rest/v2/HistoryControllerTest.java +++ b/src/src/test/java/ownradio/web/rest/v2/HistoryControllerTest.java @@ -1,14 +1,12 @@ package ownradio.web.rest.v2; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Data; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import ownradio.domain.Device; @@ -64,24 +62,16 @@ public void setUp() throws Exception { device = new Device(); } - @Data - static class HistoryDTO { - private String lastListen = "2016-12-25"; - private String isListen = "-1"; // 1, -1 - private String method = "Test"; - } - @Test public void saveStatusIsOk() throws Exception { given(this.userService.getById(USER_UUID)).willReturn(user); given(this.trackService.getById(TRACK_UUID)).willReturn(track); given(this.deviceService.getById(DEVICE_UUID)).willReturn(device); - System.out.println(mapper.writeValueAsString(new HistoryDTO())); - mockMvc.perform(post("/api/v2/histories/{deviceId}/{trackId}", DEVICE_UUID, TRACK_UUID) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(mapper.writeValueAsString(new HistoryDTO())) + .param("lastListen", "12/12/2016 11:11:11") + .param("isListen", "1") + .param("method", "method") ) .andDo(print()) .andExpect( @@ -89,7 +79,6 @@ public void saveStatusIsOk() throws Exception { ); } - @Test public void saveStatusIsInternalServerError() throws Exception { given(this.userService.getById(USER_UUID)).willReturn(user); @@ -98,10 +87,10 @@ public void saveStatusIsInternalServerError() throws Exception { doThrow(RuntimeException.class).when(this.historyService).save(any(History.class)); - mockMvc.perform(post("/api/v2/histories/{deviceId}/{trackId}", DEVICE_UUID, TRACK_UUID) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(mapper.writeValueAsString(new HistoryDTO())) + .param("lastListen", "12/12/2016 11:11:11") + .param("isListen", "1") + .param("method", "method") ) .andDo(print()) .andExpect( diff --git a/src/src/test/java/ownradio/web/rest/v2/TrackControllerTest.java b/src/src/test/java/ownradio/web/rest/v2/TrackControllerTest.java index 1b5c7c6..20469f9 100644 --- a/src/src/test/java/ownradio/web/rest/v2/TrackControllerTest.java +++ b/src/src/test/java/ownradio/web/rest/v2/TrackControllerTest.java @@ -62,8 +62,8 @@ public class TrackControllerTest { @Before public void setUp() throws Exception { - user.setId(USER_UUID); - device.setId(DEVICE_UUID); + user.setRecid(USER_UUID); + device.setRecid(DEVICE_UUID); device.setUser(user); track = new Track(PATH, device, "---"); diff --git a/v3/.gitignore b/v3/.gitignore new file mode 100644 index 0000000..eb86bec --- /dev/null +++ b/v3/.gitignore @@ -0,0 +1,6 @@ +.idea/ +logs/ +target/ +userfile/ +*.iml + diff --git a/v3/.mvn/wrapper/maven-wrapper.jar b/v3/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..5fd4d50 Binary files /dev/null and b/v3/.mvn/wrapper/maven-wrapper.jar differ diff --git a/v3/.mvn/wrapper/maven-wrapper.properties b/v3/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c954cec --- /dev/null +++ b/v3/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip diff --git a/v3/msg/masseage_en.properties b/v3/msg/masseage_en.properties new file mode 100644 index 0000000..2484f99 --- /dev/null +++ b/v3/msg/masseage_en.properties @@ -0,0 +1 @@ +id=\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u043D\u0430 en diff --git a/v3/msg/masseage_ru.properties b/v3/msg/masseage_ru.properties new file mode 100644 index 0000000..fd108d6 --- /dev/null +++ b/v3/msg/masseage_ru.properties @@ -0,0 +1 @@ +id=\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u043D\u0430 ru diff --git a/v3/mvnw b/v3/mvnw new file mode 100644 index 0000000..a1ba1bf --- /dev/null +++ b/v3/mvnw @@ -0,0 +1,233 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} "$@" diff --git a/v3/mvnw.cmd b/v3/mvnw.cmd new file mode 100644 index 0000000..2b934e8 --- /dev/null +++ b/v3/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% \ No newline at end of file diff --git a/v3/pom.xml b/v3/pom.xml new file mode 100644 index 0000000..3362a95 --- /dev/null +++ b/v3/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + kz.tanat + ownradio + 0.0.1-SNAPSHOT + jar + + OwnRadio + Серверная часть системы OwnRadio + + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + true + + + org.projectlombok + lombok + + + + com.h2database + h2 + runtime + + + org.postgresql + postgresql + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.mpatric + mp3agic + 0.8.4 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/v3/src/main/java/ownradio/Application.java b/v3/src/main/java/ownradio/Application.java new file mode 100644 index 0000000..3f37ad8 --- /dev/null +++ b/v3/src/main/java/ownradio/Application.java @@ -0,0 +1,25 @@ +package ownradio; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Главный класс приложения + * + * @author Alpenov Tanat + */ +@Slf4j +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + final ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); + + if (context.getEnvironment().getActiveProfiles().length > 0 && + "dev".equals(context.getEnvironment().getActiveProfiles()[0])) { + log.info("Open in browser: " + context.getEnvironment().getProperty("this-url")); + } + } +} diff --git a/v3/src/main/java/ownradio/annotation/DisplayName.java b/v3/src/main/java/ownradio/annotation/DisplayName.java new file mode 100644 index 0000000..0ef5e15 --- /dev/null +++ b/v3/src/main/java/ownradio/annotation/DisplayName.java @@ -0,0 +1,24 @@ +package ownradio.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Аннотация предназначена для хранения метаинформации по полю класса + * key - хранит ключ строкового значения который в зависимости от выборной лакали указывает на строку с нужным переводом + * isVisible – хранит значение видимости поля + * + * @author Alpenov Tanat + */ +@Target({METHOD, FIELD}) +@Retention(RUNTIME) +public @interface DisplayName { + + String key() default ""; + + boolean isVisible() default true; +} \ No newline at end of file diff --git a/v3/src/main/java/ownradio/command/InitDb.java b/v3/src/main/java/ownradio/command/InitDb.java new file mode 100644 index 0000000..ab61a8b --- /dev/null +++ b/v3/src/main/java/ownradio/command/InitDb.java @@ -0,0 +1,31 @@ +package ownradio.command; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import ownradio.domain.User; +import ownradio.service.UserService; + +import java.util.TimeZone; + +/** + * Класс инициализирует БД первоначальными данными + * + * @author Alpenov Tanat + */ +@Slf4j +@Profile("dev") +@Component +public class InitDb implements CommandLineRunner { + @Autowired + private UserService userService; + + @Override + public void run(String... strings) throws Exception { + TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC")); + User user = userService.save(new User()); + log.debug("User recid: {}", user.getRecid()); + } +} diff --git a/v3/src/main/java/ownradio/domain/AbstractEntity.java b/v3/src/main/java/ownradio/domain/AbstractEntity.java new file mode 100644 index 0000000..389ea09 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/AbstractEntity.java @@ -0,0 +1,70 @@ +package ownradio.domain; + +import lombok.Getter; +import lombok.Setter; +import org.hibernate.HibernateException; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.id.AbstractUUIDGenerator; +import org.hibernate.id.UUIDGenerationStrategy; +import org.hibernate.id.UUIDGenerator; +import ownradio.annotation.DisplayName; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Calendar; +import java.util.UUID; + +/** + * Базовый класс для всех сущностей + * Предназначен для хранения технической информации + * + * @author Alpenov Tanat + */ +@Getter +@Setter +@MappedSuperclass +public abstract class AbstractEntity implements Serializable { + + @DisplayName(key = "id") + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy="ownradio.util.IdOrGenerate") + @Column(unique = true) +// @Column(insertable=true, updatable=true, unique=true, nullable=false) + private UUID recid; + + private String recname; + + @Temporal(TemporalType.TIMESTAMP) + private Calendar reccreated; + + @Temporal(TemporalType.TIMESTAMP) + private Calendar recupdated; + + @PrePersist + public void beforePersist() { + setReccreated(Calendar.getInstance()); + } + + @PreUpdate + public void beforeUpdate() { + setRecupdated(Calendar.getInstance()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AbstractEntity that = (AbstractEntity) o; + + return recid != null ? recid.equals(that.recid) : that.recid == null; + + } + + @Override + public int hashCode() { + return recid != null ? recid.hashCode() : 0; + } +} \ No newline at end of file diff --git a/v3/src/main/java/ownradio/domain/Device.java b/v3/src/main/java/ownradio/domain/Device.java new file mode 100644 index 0000000..e6910df --- /dev/null +++ b/v3/src/main/java/ownradio/domain/Device.java @@ -0,0 +1,34 @@ +package ownradio.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * Сущность для хранения информации о девайсе + * + * @author Alpenov Tanat + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "devices") +public class Device extends AbstractEntity { + @ManyToOne + @JoinColumn(name = "userid") + private User user; + +// private String name; + public Device (User user, String name){ + setRecname(name); + setUser(user); + } +} diff --git a/v3/src/main/java/ownradio/domain/DownloadTrack.java b/v3/src/main/java/ownradio/domain/DownloadTrack.java new file mode 100644 index 0000000..c214eb6 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/DownloadTrack.java @@ -0,0 +1,37 @@ +package ownradio.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; +import java.util.UUID; + +/** + * Сущность для хранения информации о скаченных треках + * + * @author Alpenov Tanat + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "downloadtracks") +public class DownloadTrack extends AbstractEntity { + @ManyToOne + @JoinColumn(name = "deviceid") + private Device device; + + @ManyToOne + @JoinColumn(name = "trackid") + private Track track; + + private Integer methodid; + + private UUID userrecommend; + + private String txtrecommendinfo; + +} diff --git a/v3/src/main/java/ownradio/domain/History.java b/v3/src/main/java/ownradio/domain/History.java new file mode 100644 index 0000000..01497e3 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/History.java @@ -0,0 +1,51 @@ +package ownradio.domain; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import java.util.Calendar; +import java.util.Date; + +/** + * Сущность для хранения информации о прослушанных треках + * + * @author Alpenov Tanat + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "histories") +public class History extends AbstractEntity { + + @ManyToOne + @JoinColumn(name = "trackid") + private Track track; + + @DateTimeFormat(pattern = "dd-MM-yyyy'T'H:m:s") + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "lastlisten", nullable = false) + private Calendar lastListen; + + @Column(name = "islisten", nullable = false) + private int isListen; // 1, -1 + +// @Column(nullable = false) + private String method; + + private Integer methodid; + + @ManyToOne + @JoinColumn(name = "deviceid") + private Device device; + + private Integer countsend; + + private String comment; +} diff --git a/v3/src/main/java/ownradio/domain/NextTrack.java b/v3/src/main/java/ownradio/domain/NextTrack.java new file mode 100644 index 0000000..906b320 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/NextTrack.java @@ -0,0 +1,20 @@ +package ownradio.domain; + +import lombok.Getter; +import lombok.Setter; + +import java.sql.Time; +import java.util.UUID; + +/** + * Created by a.polunina on 29.11.2016. + */ +@Getter +@Setter +public class NextTrack { + private UUID trackid; + private Integer methodid; + private UUID useridrecommended; + private String txtrecommendedinfo; + private String timeexecute; +} diff --git a/v3/src/main/java/ownradio/domain/Rating.java b/v3/src/main/java/ownradio/domain/Rating.java new file mode 100644 index 0000000..02e753f --- /dev/null +++ b/v3/src/main/java/ownradio/domain/Rating.java @@ -0,0 +1,41 @@ +package ownradio.domain; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import java.util.Calendar; +import java.util.Date; + +/** + * Сущность для хранения информации о рейтингах треков + * + * @author Alpenov Tanat + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "ratings") +public class Rating extends AbstractEntity { + @ManyToOne + @JoinColumn(name = "userid") + private User user; + + @ManyToOne + @JoinColumn(name = "trackid") + private Track track; + + @DateTimeFormat(pattern = "dd-MM-yyyy'T'H:m:s") + @Column(nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Calendar lastlisten; + + @Column(nullable = false) + private Integer ratingsum; +} diff --git a/v3/src/main/java/ownradio/domain/Ratio.java b/v3/src/main/java/ownradio/domain/Ratio.java new file mode 100644 index 0000000..1a8c652 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/Ratio.java @@ -0,0 +1,33 @@ +package ownradio.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +/** + * Сущность для хранения коэффициентов схожести интересов + * + * Created by a.polunina on 16.02.2017. + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "ratios") +public class Ratio extends AbstractEntity { +// @ManyToOne + @JoinColumn(name = "userid") + @Column(nullable = false) + private User userid1; + +// @ManyToOne + @JoinColumn(name = "userid") + @Column(nullable = false) + private User userid2; + + private Integer ratio; +} diff --git a/v3/src/main/java/ownradio/domain/Track.java b/v3/src/main/java/ownradio/domain/Track.java new file mode 100644 index 0000000..1721b74 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/Track.java @@ -0,0 +1,46 @@ +package ownradio.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +/** + * Сущность для хранения информации о треке + * + * @author Alpenov Tanat + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "tracks") +public class Track extends AbstractEntity { + + private String path; + + @ManyToOne + @JoinColumn(name = "deviceid") + private Device device; + + @Column(nullable = false) + private String localdevicepathupload; + + private Integer length; + + private String artist; + + private Integer size; + + private Integer iscorrect; + + private Integer isfilledinfo; + + private Integer iscensorial; + + private Integer isexist; + +} diff --git a/v3/src/main/java/ownradio/domain/TracksHistory.java b/v3/src/main/java/ownradio/domain/TracksHistory.java new file mode 100644 index 0000000..bce7faa --- /dev/null +++ b/v3/src/main/java/ownradio/domain/TracksHistory.java @@ -0,0 +1,18 @@ +package ownradio.domain; + +import lombok.Getter; +import lombok.Setter; + +/** + * + * Сущность для хранения информации об отданных пользователю треках и истории по ним + * + * Created by a.polunina on 07.04.2017. + */ +@Setter +@Getter +public class TracksHistory { + private DownloadTrack downloadTrack; + private History history; + +} diff --git a/v3/src/main/java/ownradio/domain/User.java b/v3/src/main/java/ownradio/domain/User.java new file mode 100644 index 0000000..e0d4117 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/User.java @@ -0,0 +1,25 @@ +package ownradio.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * Сущность для хранения информации о пользователе + * + * @author Alpenov Tanat + */ +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "users") +public class User extends AbstractEntity { + public User(String name) { + setRecname(name); + } + public Integer experience; +} diff --git a/v3/src/main/java/ownradio/domain/UsersRating.java b/v3/src/main/java/ownradio/domain/UsersRating.java new file mode 100644 index 0000000..2dffb55 --- /dev/null +++ b/v3/src/main/java/ownradio/domain/UsersRating.java @@ -0,0 +1,35 @@ +package ownradio.domain; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigInteger; +import java.util.UUID; + +/** + * Created by a.polunina on 22.03.2017. + */ +@Setter +@Getter +public class UsersRating{ + private UUID userid; + + @DateTimeFormat(pattern = "dd-MM-yyyy'T'H:m:s") + //@Temporal(TemporalType.TIMESTAMP) + private String reccreated; + + private String recname; + + @DateTimeFormat(pattern = "dd-MM-yyyy'T'H:m:s") +// @Temporal(TemporalType.TIMESTAMP) + private String recupdated; + + @DateTimeFormat(pattern = "dd-MM-yyyy'T'H:m:s") + //@Temporal(TemporalType.TIMESTAMP) + private String lastactive; + + private BigInteger owntracks; + + private BigInteger downloadtracks; +} diff --git a/v3/src/main/java/ownradio/repository/DeviceRepository.java b/v3/src/main/java/ownradio/repository/DeviceRepository.java new file mode 100644 index 0000000..0a99760 --- /dev/null +++ b/v3/src/main/java/ownradio/repository/DeviceRepository.java @@ -0,0 +1,24 @@ +package ownradio.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ownradio.domain.Device; + +import java.util.List; +import java.util.UUID; + +/** + * Интерфейс репозитория, для хранения девайсов + * + * @author Alpenov Tanat + */ +public interface DeviceRepository extends JpaRepository { + @Query(value = "select * from devices where userid = ?1", nativeQuery = true) + List getUserDevices(UUID userid); + + @Query(value = "select * from registerdevice(?1, ?2)", nativeQuery = true) + boolean registerdevice(UUID deviceId, String deviceName); + + @Query(value = "select * from getlastdevices()", nativeQuery = true) + List getLastDevices(); +} diff --git a/v3/src/main/java/ownradio/repository/DownloadTrackRepository.java b/v3/src/main/java/ownradio/repository/DownloadTrackRepository.java new file mode 100644 index 0000000..a42b608 --- /dev/null +++ b/v3/src/main/java/ownradio/repository/DownloadTrackRepository.java @@ -0,0 +1,27 @@ +package ownradio.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ownradio.domain.Device; +import ownradio.domain.DownloadTrack; +import ownradio.domain.Track; + +import java.util.List; +import java.util.UUID; + +/** + * Интерфейс репозитория, для хранения истории скаченных треков + * + * @author Alpenov Tanat + */ +public interface DownloadTrackRepository extends JpaRepository { + + DownloadTrack findFirstByDeviceAndTrackOrderByReccreatedAsc(Device device, Track track); + + @Query(value = "select * from getlasttracks(?1, ?2)", nativeQuery = true) + List getLastTracksByDevice(UUID deviceid, Integer countTracks); + + @Query(value = "select * from getTracksHistoryByDevice(?1, ?2)", nativeQuery = true) + List getTracksHistoryByDevice(UUID deviceId, Integer countRows); + +} diff --git a/v3/src/main/java/ownradio/repository/HistoryRepository.java b/v3/src/main/java/ownradio/repository/HistoryRepository.java new file mode 100644 index 0000000..50251fc --- /dev/null +++ b/v3/src/main/java/ownradio/repository/HistoryRepository.java @@ -0,0 +1,14 @@ +package ownradio.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ownradio.domain.History; + +import java.util.UUID; + +/** + * Интерфейс репозитория, для хранения истории прослушанных треков + * + * @author Alpenov Tanat + */ +public interface HistoryRepository extends JpaRepository { +} diff --git a/v3/src/main/java/ownradio/repository/RatingRepository.java b/v3/src/main/java/ownradio/repository/RatingRepository.java new file mode 100644 index 0000000..b739402 --- /dev/null +++ b/v3/src/main/java/ownradio/repository/RatingRepository.java @@ -0,0 +1,22 @@ +package ownradio.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ownradio.domain.Rating; +import ownradio.domain.Track; +import ownradio.domain.User; + +import java.util.Date; +import java.util.UUID; + +/** + * Интерфейс репозитория, для хранения рейтингов прослушанных треков + * + * @author Alpenov Tanat + */ +public interface RatingRepository extends JpaRepository { + + Rating findByUser(User user); + + Rating findByUserAndTrack(User user, Track track); +} diff --git a/v3/src/main/java/ownradio/repository/RatioRepository.java b/v3/src/main/java/ownradio/repository/RatioRepository.java new file mode 100644 index 0000000..4232cdc --- /dev/null +++ b/v3/src/main/java/ownradio/repository/RatioRepository.java @@ -0,0 +1,18 @@ +package ownradio.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ownradio.domain.Ratio; + +import java.util.UUID; + +/** + * Интерфейс репозитория, для хранения коэффициентов схожести интересов + * + * Created by a.polunina on 16.02.2017. + */ +public interface RatioRepository extends JpaRepository { + @Query(value = "select updateratios(?1)", nativeQuery = true) + boolean updateRatios(UUID deviceId); + +} \ No newline at end of file diff --git a/v3/src/main/java/ownradio/repository/TrackRepository.java b/v3/src/main/java/ownradio/repository/TrackRepository.java new file mode 100644 index 0000000..c0c3b77 --- /dev/null +++ b/v3/src/main/java/ownradio/repository/TrackRepository.java @@ -0,0 +1,25 @@ +package ownradio.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ownradio.domain.Track; + +import java.util.List; +import java.util.UUID; + +/** + * Интерфейс репозитория, для хранения треков + * + * @author Alpenov Tanat + */ +public interface TrackRepository extends JpaRepository { + @Query(value = "select getnexttrackid_string(?1)", nativeQuery = true) + UUID getNextTrackId(UUID deviceId); + + @Query(value = "select * from getnexttrack_v2(?1)", nativeQuery = true) +// @Query(value = "select * from getnexttrack(?1)", nativeQuery = true) + List getNextTrackV2(UUID deviceId); + + @Query(value = "select registertrack(?1, ?2, ?3, ?4)", nativeQuery = true) + boolean registerTrack(UUID trackId, String localDevicePathUpload, String path, UUID deviceId); +} diff --git a/v3/src/main/java/ownradio/repository/UserRepository.java b/v3/src/main/java/ownradio/repository/UserRepository.java new file mode 100644 index 0000000..04c426a --- /dev/null +++ b/v3/src/main/java/ownradio/repository/UserRepository.java @@ -0,0 +1,22 @@ +package ownradio.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ownradio.domain.User; + +import java.util.List; +import java.util.UUID; + +/** + * Интерфейс репозитория, для хранения данных пользователя + * + * @author Alpenov Tanat + */ +public interface UserRepository extends JpaRepository { + + @Query(value = "select * from getusersrating(?1)", nativeQuery = true) + List getUsersRating(Integer countRows); + + @Query(value = "select * from getlastusers(?1)", nativeQuery = true) + List getLastUsers(Integer countRows); +} diff --git a/v3/src/main/java/ownradio/service/DeviceService.java b/v3/src/main/java/ownradio/service/DeviceService.java new file mode 100644 index 0000000..40321c5 --- /dev/null +++ b/v3/src/main/java/ownradio/service/DeviceService.java @@ -0,0 +1,22 @@ +package ownradio.service; + +import org.springframework.data.jpa.repository.Query; +import ownradio.domain.Device; + +import java.util.List; +import java.util.UUID; + +/** + * Интерфейс сервиса, для работы с девайсами + * + * @author Alpenov Tanat + */ +public interface DeviceService { + void save(Device device); + + Device getById(UUID uuid); + + List getByUserid(UUID userid); + + List getLastDevices(); +} diff --git a/v3/src/main/java/ownradio/service/DownloadTrackService.java b/v3/src/main/java/ownradio/service/DownloadTrackService.java new file mode 100644 index 0000000..79905ae --- /dev/null +++ b/v3/src/main/java/ownradio/service/DownloadTrackService.java @@ -0,0 +1,20 @@ +package ownradio.service; + +import ownradio.domain.DownloadTrack; +import ownradio.domain.TracksHistory; + +import java.util.List; +import java.util.UUID; + +/** + * Интерфейс сервиса, для работы с историей скаченных треков + * + * @author Alpenov Tanat + */ +public interface DownloadTrackService { + void save(DownloadTrack downloadTrack); + + List getLastTracksByDevice(UUID deviceId, Integer countTracks); + + List getTracksHistoryByDevice(UUID deviceId, Integer countRows); +} diff --git a/v3/src/main/java/ownradio/service/HistoryService.java b/v3/src/main/java/ownradio/service/HistoryService.java new file mode 100644 index 0000000..11ed099 --- /dev/null +++ b/v3/src/main/java/ownradio/service/HistoryService.java @@ -0,0 +1,15 @@ +package ownradio.service; + +import ownradio.domain.History; + +import java.util.UUID; + +/** + * Интерфейс сервиса, для работы с историей прослушанных треков + * + * @author Alpenov Tanat + */ +public interface HistoryService { + void save(History history, Boolean isNewHistoryRec); + History getById(UUID uuid); +} diff --git a/v3/src/main/java/ownradio/service/RatingService.java b/v3/src/main/java/ownradio/service/RatingService.java new file mode 100644 index 0000000..b06a3da --- /dev/null +++ b/v3/src/main/java/ownradio/service/RatingService.java @@ -0,0 +1,12 @@ +package ownradio.service; + +import ownradio.domain.Rating; + +/** + * Интерфейс сервиса, для работы с рейтингами прослушанных треков + * + * @author Alpenov Tanat + */ +public interface RatingService { + void save(Rating rating); +} diff --git a/v3/src/main/java/ownradio/service/RatioService.java b/v3/src/main/java/ownradio/service/RatioService.java new file mode 100644 index 0000000..1581c63 --- /dev/null +++ b/v3/src/main/java/ownradio/service/RatioService.java @@ -0,0 +1,16 @@ +package ownradio.service; + +import ownradio.domain.Ratio; + +import java.util.UUID; + +/** + * Интерфейс сервиса, для работы с коэффициентами схожести интересов пользователей + * + * Created by a.polunina on 16.02.2017. + */ +public interface RatioService { + void save (Ratio ratio); + + void updateRatios(UUID deviceId); +} diff --git a/v3/src/main/java/ownradio/service/TrackService.java b/v3/src/main/java/ownradio/service/TrackService.java new file mode 100644 index 0000000..4a10806 --- /dev/null +++ b/v3/src/main/java/ownradio/service/TrackService.java @@ -0,0 +1,25 @@ +package ownradio.service; + +import org.springframework.web.multipart.MultipartFile; +import ownradio.domain.NextTrack; +import ownradio.domain.Track; + +import java.util.UUID; + +/** + * Интерфейс сервиса, для работы с треками + * + * @author Alpenov Tanat + */ +public interface TrackService { + + Track getById(UUID id); + + UUID getNextTrackId(UUID deviceId); + + NextTrack getNextTrackIdV2(UUID deviceId); + + void save(Track track, MultipartFile file); + + void setTrackInfo(UUID trackid); +} diff --git a/v3/src/main/java/ownradio/service/UserService.java b/v3/src/main/java/ownradio/service/UserService.java new file mode 100644 index 0000000..9e4392b --- /dev/null +++ b/v3/src/main/java/ownradio/service/UserService.java @@ -0,0 +1,22 @@ +package ownradio.service; + +import ownradio.domain.User; +import ownradio.domain.UsersRating; + +import java.util.List; +import java.util.UUID; + +/** + * Интерфейс сервиса, для работы с пользовательскими данными + * + * @author Alpenov Tanat + */ +public interface UserService { + User getById(UUID id); + + User save(User user); + + List getUsersRating(Integer countRows); + + List getLastUsers(Integer countRows); +} diff --git a/v3/src/main/java/ownradio/service/impl/DeviceServiceImpl.java b/v3/src/main/java/ownradio/service/impl/DeviceServiceImpl.java new file mode 100644 index 0000000..5a1631d --- /dev/null +++ b/v3/src/main/java/ownradio/service/impl/DeviceServiceImpl.java @@ -0,0 +1,51 @@ +package ownradio.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ownradio.domain.Device; +import ownradio.domain.UsersRating; +import ownradio.repository.DeviceRepository; +import ownradio.service.DeviceService; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Service +public class DeviceServiceImpl implements DeviceService { + + private final DeviceRepository deviceRepository; + + @Autowired + public DeviceServiceImpl(DeviceRepository deviceRepository) { + this.deviceRepository = deviceRepository; + } + + + @Override + public void save(Device device) { + deviceRepository.saveAndFlush(device); + } + + @Override + public Device getById(UUID uuid) { + return deviceRepository.findOne(uuid); + } + + @Override + public List getByUserid(UUID uuid) { + return deviceRepository.getUserDevices(uuid); + } + + @Override + public List getLastDevices(){ + List lastDevices = new ArrayList(); + List objects = deviceRepository.getLastDevices(); + if (objects != null) { + for (int i = 0; i < objects.size(); i++) { + lastDevices.add(getById(UUID.fromString((String) objects.get(i)))); + } + } + return lastDevices; + } +} diff --git a/v3/src/main/java/ownradio/service/impl/DownloadTrackServiceImpl.java b/v3/src/main/java/ownradio/service/impl/DownloadTrackServiceImpl.java new file mode 100644 index 0000000..fdaec20 --- /dev/null +++ b/v3/src/main/java/ownradio/service/impl/DownloadTrackServiceImpl.java @@ -0,0 +1,57 @@ +package ownradio.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ownradio.domain.DownloadTrack; +import ownradio.domain.TracksHistory; +import ownradio.repository.DownloadTrackRepository; +import ownradio.repository.HistoryRepository; +import ownradio.service.DownloadTrackService; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Service +public class DownloadTrackServiceImpl implements DownloadTrackService { + + private final DownloadTrackRepository downloadTrackRepository; + private HistoryRepository historyRepository; + + @Autowired + public DownloadTrackServiceImpl(DownloadTrackRepository downloadTrackRepository, HistoryRepository historyRepository) { + this.downloadTrackRepository = downloadTrackRepository; + this.historyRepository = historyRepository; + } + + @Override + public void save(DownloadTrack downloadTrack) { + downloadTrackRepository.saveAndFlush(downloadTrack); + } + + @Override + @Transactional + public List getLastTracksByDevice(UUID deviceId, Integer countTracks) { + return downloadTrackRepository.getLastTracksByDevice(deviceId, countTracks); + } + + @Override + @Transactional + public List getTracksHistoryByDevice(UUID deviceId, Integer countRows){ + List tracksHistories = new ArrayList(); + List objects = downloadTrackRepository.getTracksHistoryByDevice(deviceId, countRows); + if (objects != null) { + for (int i = 0; i < objects.size(); i++) { + TracksHistory tracksHistory = new TracksHistory(); + tracksHistory.setDownloadTrack(downloadTrackRepository.findOne(UUID.fromString((String) objects.get(i)[0]))); + if((String) objects.get(i)[1] != null) tracksHistory.setHistory(historyRepository.findOne(UUID.fromString((String) objects.get(i)[1]))); + + tracksHistories.add(tracksHistory); + } + } else { + return null; + } + return tracksHistories; + } +} diff --git a/v3/src/main/java/ownradio/service/impl/HistoryServiceImpl.java b/v3/src/main/java/ownradio/service/impl/HistoryServiceImpl.java new file mode 100644 index 0000000..c080d2e --- /dev/null +++ b/v3/src/main/java/ownradio/service/impl/HistoryServiceImpl.java @@ -0,0 +1,62 @@ +package ownradio.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ownradio.domain.History; +import ownradio.domain.Rating; +import ownradio.repository.HistoryRepository; +import ownradio.repository.RatingRepository; +import ownradio.repository.RatioRepository; +import ownradio.service.HistoryService; + +import java.util.UUID; + +@Service +public class HistoryServiceImpl implements HistoryService { + + private final HistoryRepository historyRepository; + private final RatingRepository ratingRepository; + private final RatioRepository ratioRepository; + + @Autowired + public HistoryServiceImpl(HistoryRepository historyRepository, RatingRepository ratingRepository, RatioRepository ratioRepository) { + this.historyRepository = historyRepository; + this.ratingRepository = ratingRepository; + this.ratioRepository = ratioRepository; + } + + @Override + public History getById(UUID uuid) { + return historyRepository.findOne(uuid); + } + + @Transactional + @Override + public void save(History history, Boolean isNewHistoryRec) { + historyRepository.saveAndFlush(history); + + if(isNewHistoryRec) { + Rating rating = ratingRepository.findByUserAndTrack(history.getDevice().getUser(), history.getTrack()); + if (rating != null) { + int ratingsum = rating.getRatingsum() + history.getIsListen(); + rating.setLastlisten(history.getLastListen()); + rating.setRatingsum(ratingsum); + ratingRepository.saveAndFlush(rating); + } else { + rating = new Rating(); + rating.setUser(history.getDevice().getUser()); + rating.setTrack(history.getTrack()); + rating.setLastlisten(history.getLastListen()); + rating.setRatingsum(history.getIsListen()); + ratingRepository.saveAndFlush(rating); + } + + try { + ratioRepository.updateRatios(history.getDevice().getRecid()); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } +} diff --git a/v3/src/main/java/ownradio/service/impl/RatingServiceImpl.java b/v3/src/main/java/ownradio/service/impl/RatingServiceImpl.java new file mode 100644 index 0000000..2abce5f --- /dev/null +++ b/v3/src/main/java/ownradio/service/impl/RatingServiceImpl.java @@ -0,0 +1,24 @@ +package ownradio.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ownradio.domain.Rating; +import ownradio.repository.RatingRepository; +import ownradio.service.RatingService; + +@Service +public class RatingServiceImpl implements RatingService { + + private final RatingRepository ratingRepository; + + @Autowired + public RatingServiceImpl(RatingRepository ratingRepository) { + this.ratingRepository = ratingRepository; + } + + + @Override + public void save(Rating rating) { + ratingRepository.saveAndFlush(rating); + } +} diff --git a/v3/src/main/java/ownradio/service/impl/RatioServiceImpl.java b/v3/src/main/java/ownradio/service/impl/RatioServiceImpl.java new file mode 100644 index 0000000..fce00a3 --- /dev/null +++ b/v3/src/main/java/ownradio/service/impl/RatioServiceImpl.java @@ -0,0 +1,36 @@ +package ownradio.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import ownradio.domain.Ratio; +import ownradio.repository.RatioRepository; +import ownradio.service.RatioService; + +import java.util.UUID; + +/** + * Created by a.polunina on 16.02.2017. + */ +public class RatioServiceImpl implements RatioService{ + + private final RatioRepository ratioRepository; + + @Autowired + public RatioServiceImpl(RatioRepository ratioRepository) { + this.ratioRepository = ratioRepository; + } + + @Override + public void save(Ratio ratio) { + ratioRepository.saveAndFlush(ratio); + } + + @Override + public void updateRatios(UUID deviceId){ + try { + ratioRepository.updateRatios(deviceId); + }catch (Exception ex){ + ex.printStackTrace(); + } + + } +} diff --git a/v3/src/main/java/ownradio/service/impl/TrackServiceImpl.java b/v3/src/main/java/ownradio/service/impl/TrackServiceImpl.java new file mode 100644 index 0000000..cbe381e --- /dev/null +++ b/v3/src/main/java/ownradio/service/impl/TrackServiceImpl.java @@ -0,0 +1,122 @@ +package ownradio.service.impl; + +import com.mpatric.mp3agic.ID3v1; +import com.mpatric.mp3agic.ID3v2; +import com.mpatric.mp3agic.Mp3File; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; +import ownradio.domain.NextTrack; +import ownradio.domain.Track; +import ownradio.repository.TrackRepository; +import ownradio.service.TrackService; +import ownradio.util.DecodeUtil; +import ownradio.util.ResourceUtil; + +import java.util.List; +import java.util.UUID; + +@Service +public class TrackServiceImpl implements TrackService { + + private final TrackRepository trackRepository; + + @Autowired + public TrackServiceImpl(TrackRepository trackRepository) { + this.trackRepository = trackRepository; + } + + @Override + @Transactional(readOnly = true) + public Track getById(UUID id) { + return trackRepository.findOne(id); + } + + @Override + @Transactional + public UUID getNextTrackId(UUID deviceId) { + return trackRepository.getNextTrackId(deviceId); + } + + @Override + @Transactional + public NextTrack getNextTrackIdV2(UUID deviceId) { + NextTrack nextTrack = new NextTrack(); + List objects = trackRepository.getNextTrackV2(deviceId); + if(objects != null) { + nextTrack.setTrackid(UUID.fromString((String) objects.get(0)[0])); + nextTrack.setMethodid((Integer) objects.get(0)[1]); + if(objects.get(0)[2] != null) nextTrack.setUseridrecommended(UUID.fromString((String) objects.get(0)[2])); + if(objects.get(0)[3] != null) nextTrack.setTxtrecommendedinfo((String) objects.get(0)[3]); + if(objects.get(0)[4] != null) nextTrack.setTimeexecute((String) objects.get(0)[4]); + return nextTrack; + }else{ + return null; + } + } + + @Override + @Transactional + public void save(Track track, MultipartFile file) { + boolean result = trackRepository.registerTrack(track.getRecid(), track.getLocaldevicepathupload(), track.getPath(), track.getDevice().getRecid()); + if (!result) { + throw new RuntimeException(); + } + + Track storeTrack = trackRepository.findOne(track.getRecid()); + + String dirName = storeTrack.getDevice().getUser().getRecid().toString(); + String fileName = storeTrack.getRecid() + "." + StringUtils.getFilenameExtension(file.getOriginalFilename()); + String filePath = ResourceUtil.save(dirName, fileName, file); + + storeTrack.setPath(filePath); + } + + @Override + @Transactional + public void setTrackInfo(UUID trackid) { + String artist = null; + String title = null; + boolean artistFlag = false; + boolean titleFlag = false; + + if (trackid != null) { + try { + Track track = trackRepository.findOne(trackid); + Mp3File mp3File = new Mp3File(track.getPath()); + track.setLength((int) mp3File.getLengthInSeconds());//duration track + track.setSize((int) mp3File.getLength() / 1024);//size in kilobytes + + if (mp3File.hasId3v2Tag()) { + ID3v2 id3v2Tag2 = mp3File.getId3v2Tag(); + title = DecodeUtil.Decode(id3v2Tag2.getTitle()); + artist = DecodeUtil.Decode(id3v2Tag2.getArtist()); + } + if(mp3File.hasId3v1Tag()) { + ID3v1 id3v1Tag1 = mp3File.getId3v1Tag(); + if(title == null || title.equals("null") || title.isEmpty()) + title = DecodeUtil.Decode(id3v1Tag1.getTitle()); + if(artist == null || artist.equals("null") || artist.isEmpty()) + artist = DecodeUtil.Decode(id3v1Tag1.getArtist()); + } + + if (title != null && !title.equals("null") && !title.isEmpty()) { + track.setRecname(title.replaceAll("\u0000", "")); + titleFlag = true; + } + if (artist != null && !artist.equals("null") && !artist.isEmpty()) { + track.setArtist(artist.replaceAll("\u0000", "")); + artistFlag = true; + } + + if (artistFlag && titleFlag) + track.setIsfilledinfo(1); + trackRepository.saveAndFlush(track); + } catch (Exception ex) { +// ex.printStackTrace(); + } + } + } +} diff --git a/v3/src/main/java/ownradio/service/impl/UserServiceImpl.java b/v3/src/main/java/ownradio/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..b07389f --- /dev/null +++ b/v3/src/main/java/ownradio/service/impl/UserServiceImpl.java @@ -0,0 +1,77 @@ +package ownradio.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ownradio.domain.Device; +import ownradio.domain.User; +import ownradio.domain.UsersRating; +import ownradio.repository.UserRepository; +import ownradio.service.UserService; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Service +public class UserServiceImpl implements UserService { + @Autowired + private UserRepository userRepository; + + @Override + public User getById(UUID id) { + return userRepository.findOne(id); + } + + @Override + public User save(User user) { + return userRepository.saveAndFlush(user); + } + + @Override + @Transactional + public List getUsersRating(Integer countRows) { + List usersRating = new ArrayList(); + List objects = userRepository.getUsersRating(countRows); + if (objects != null) { + for (int i = 0; i < objects.size(); i++) { + UsersRating userRating = new UsersRating(); + userRating.setUserid(UUID.fromString((String) objects.get(i)[0])); + userRating.setReccreated((String) objects.get(i)[1]); + userRating.setRecname((String) objects.get(i)[2]); + userRating.setRecupdated((String) objects.get(i)[3]); + userRating.setOwntracks((BigInteger) objects.get(i)[4]); + userRating.setDownloadtracks((BigInteger) objects.get(i)[5]); + + usersRating.add(userRating); + } + } else { + return null; + } + return usersRating; + } + + + @Override + public List getLastUsers(Integer countRows){ + List lastUsers = new ArrayList(); + List objects = userRepository.getLastUsers(countRows); + if (objects != null) { + for (int i = 0; i < objects.size(); i++) { + UsersRating lastUser = new UsersRating(); + lastUser.setUserid(UUID.fromString((String) objects.get(i)[0])); + lastUser.setReccreated((String) objects.get(i)[1]); + lastUser.setLastactive((String) objects.get(i)[2]); + lastUser.setRecname((String) objects.get(i)[3]); + lastUser.setRecupdated((String) objects.get(i)[4]); + lastUser.setOwntracks((BigInteger) objects.get(i)[5]); + lastUser.setDownloadtracks((BigInteger) objects.get(i)[6]); + lastUsers.add(lastUser); + } + } else { + return null; + } + return lastUsers; + } +} diff --git a/v3/src/main/java/ownradio/util/DecodeUtil.java b/v3/src/main/java/ownradio/util/DecodeUtil.java new file mode 100644 index 0000000..12dda7a --- /dev/null +++ b/v3/src/main/java/ownradio/util/DecodeUtil.java @@ -0,0 +1,25 @@ +package ownradio.util; + +import java.io.UnsupportedEncodingException; + +/** + * Created by a.polunina on 11.01.2017. + * Класс утилита для декодирования строк + */ +public class DecodeUtil { + + //Функция декодирует "Cp1252" в "Cp1251" + public static String Decode(String str) { + if(str == null) + return str; + try { + if (str.chars().anyMatch(c -> c >= 'А' - 848 && c <= 'ё' - 848)) { + return new String(str.getBytes("Cp1252"), "Cp1251"); + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return str; + } +} diff --git a/v3/src/main/java/ownradio/util/IdOrGenerate.java b/v3/src/main/java/ownradio/util/IdOrGenerate.java new file mode 100644 index 0000000..8d17e5d --- /dev/null +++ b/v3/src/main/java/ownradio/util/IdOrGenerate.java @@ -0,0 +1,28 @@ +package ownradio.util; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.id.UUIDGenerator; +import ownradio.domain.AbstractEntity; + +import java.util.UUID; + +/** + * Created by a.polunina on 17.04.2017. + */ + + public class IdOrGenerate extends UUIDGenerator { + + @Override + public UUID generate(SessionImplementor session, Object obj) throws HibernateException { + if (obj == null) throw new HibernateException(new NullPointerException()); + + if (((AbstractEntity) obj).getRecid() == null) { + UUID id = UUID.randomUUID(); // super.generate(session, obj); + return id; + } else { + return ((AbstractEntity) obj).getRecid(); + + } + } + } \ No newline at end of file diff --git a/v3/src/main/java/ownradio/util/ReflectUtil.java b/v3/src/main/java/ownradio/util/ReflectUtil.java new file mode 100644 index 0000000..3e37036 --- /dev/null +++ b/v3/src/main/java/ownradio/util/ReflectUtil.java @@ -0,0 +1,51 @@ +package ownradio.util; + +import ownradio.annotation.DisplayName; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.*; + +/** + * Класс утилита для упрощения работы с рефлексией + * + * @author Alpenov Tanat + */ +public class ReflectUtil { + private ReflectUtil() { + } + + private static List getAllFields(List fields, Class type) { + fields.addAll(Arrays.asList(type.getDeclaredFields())); + + if (type.getSuperclass() != null) { + fields = getAllFields(fields, type.getSuperclass()); + } + + return fields; + } + + public static Map getDisplayNameFields(Object o) { + ResourceBundle res = ResourceUtil.getResourceBundle(); + + Map result = new HashMap<>(); + + getAllFields(new LinkedList<>(), o.getClass()).stream() + .filter(field -> field.isAnnotationPresent(DisplayName.class)) + .forEach(field -> { + for (Annotation annotation : field.getAnnotations()) { + if (annotation instanceof DisplayName) { + DisplayName displayName = (DisplayName) annotation; + + if (displayName.isVisible()) { + String key = displayName.key(); + result.put(key, res.getString(key)); + } + } + } + + }); + + return result; + } +} diff --git a/v3/src/main/java/ownradio/util/ResourceUtil.java b/v3/src/main/java/ownradio/util/ResourceUtil.java new file mode 100644 index 0000000..06ffb6d --- /dev/null +++ b/v3/src/main/java/ownradio/util/ResourceUtil.java @@ -0,0 +1,69 @@ +package ownradio.util; + +import org.springframework.util.ResourceUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * Класс утилита для упрощения работы с ресурсами + * + * @author Alpenov Tanat + */ +public class ResourceUtil { + public static final String APP_DIR = System.getProperty("user.dir"); + public static final String UPLOAD_DIR = System.getProperty("upload.dir") == null ? APP_DIR + "/userfile/" : System.getProperty("upload.dir"); + public static final String MESSAGE_DIR = APP_DIR + "/msg"; + public static final String MESSAGE_BASE_NAME = "masseage"; + + private ResourceUtil() { + } + + public static byte[] read(String fileName) { + File file; + try { + file = ResourceUtils.getFile(fileName); + return Files.readAllBytes(file.toPath()); + } catch (IOException e) { + throw new RuntimeException("Error read resource"); + } + } + + public static String save(String userDir, String fileName, MultipartFile file) { + try { + File dir = new File(UPLOAD_DIR + userDir); + if (!dir.exists()) { + dir.mkdirs(); + } + + String filePath = Paths.get(UPLOAD_DIR, userDir, fileName).toString(); + file.transferTo(new File(filePath)); + + return filePath; + } catch (Exception e) { + throw new RuntimeException("Error save file", e); + } + } + + public static ResourceBundle getResourceBundle() { + File file = new File(MESSAGE_DIR); + URL[] urls; + try { + urls = new URL[]{file.toURI().toURL()}; + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + ClassLoader loader = new URLClassLoader(urls); + + return ResourceBundle.getBundle(MESSAGE_BASE_NAME, Locale.getDefault(), loader); + } + +} diff --git a/v3/src/main/java/ownradio/web/rest/v2/HistoryController.java b/v3/src/main/java/ownradio/web/rest/v2/HistoryController.java new file mode 100644 index 0000000..9c3093a --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v2/HistoryController.java @@ -0,0 +1,69 @@ +package ownradio.web.rest.v2; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import ownradio.domain.Device; +import ownradio.domain.History; +import ownradio.domain.Track; +import ownradio.domain.User; +import ownradio.service.DeviceService; +import ownradio.service.HistoryService; +import ownradio.service.TrackService; +import ownradio.service.UserService; + +import javax.persistence.Column; +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.UUID; + +/** + * WEB API для работы с историей прослушанных треков + * + * @author Alpenov Tanat + */ +@Slf4j +@RestController +@RequestMapping("/v2/histories") +public class HistoryController { + private final HistoryService historyService; + private final TrackService trackService; + private final DeviceService deviceService; + + @Autowired + public HistoryController(HistoryService historyService, TrackService trackService, DeviceService deviceService) { + this.historyService = historyService; + this.trackService = trackService; + this.deviceService = deviceService; + } + + @RequestMapping(value = "/{deviceId}/{trackId}", method = RequestMethod.POST) + public ResponseEntity save(@PathVariable UUID deviceId, @PathVariable UUID trackId, @RequestBody History history) { + try { + log.info("{} {}",deviceId.toString(),trackId.toString()); + log.info("{} {}",history.getLastListen(), history.getIsListen()); + Track track = trackService.getById(trackId); + Device device = deviceService.getById(deviceId); + + if (track == null || device == null) { + throw new RuntimeException("Track or Device is null"); + } + + history.setTrack(track); + history.setDevice(device); + + historyService.save(history, true); + + return new ResponseEntity(history, HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity(history, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/v3/src/main/java/ownradio/web/rest/v2/TrackController.java b/v3/src/main/java/ownradio/web/rest/v2/TrackController.java new file mode 100644 index 0000000..1dbd30f --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v2/TrackController.java @@ -0,0 +1,102 @@ +package ownradio.web.rest.v2; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import ownradio.domain.Device; +import ownradio.domain.Track; +import ownradio.service.TrackService; +import ownradio.util.ResourceUtil; + +import java.util.UUID; + +/** + * WEB API для работы с треками + * + * @author Alpenov Tanat + */ +@Slf4j +@RestController +@RequestMapping(value = "/v2/tracks") +public class TrackController { + + private final TrackService trackService; + + @Autowired + public TrackController(TrackService trackService) { + this.trackService = trackService; + } + + @Data + private static class TrackDTO { + private UUID fileGuid; + private String fileName; + private String filePath; + private UUID deviceId; + private MultipartFile musicFile; + + public Track getTrack() { + Device device = new Device(); + device.setRecid(deviceId); + + Track track = new Track(); + track.setRecid(fileGuid); + track.setRecname(fileName); + track.setDevice(device); + track.setPath("---"); + track.setLocaldevicepathupload(filePath); + + return track; + } + } + + @RequestMapping(method = RequestMethod.POST) + public ResponseEntity save(TrackDTO trackDTO) { + if (trackDTO.getMusicFile().isEmpty()) { + return new ResponseEntity(HttpStatus.BAD_REQUEST); + } + + try { + trackService.save(trackDTO.getTrack(), trackDTO.getMusicFile()); + + return new ResponseEntity(HttpStatus.CREATED); + } catch (Exception e) { + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + + } + + @RequestMapping(value = "/{id}", method = RequestMethod.GET) + public ResponseEntity getTrack(@PathVariable UUID id) { + Track track = trackService.getById(id); + + if (track != null) { + byte[] bytes = ResourceUtil.read(track.getPath()); + return new ResponseEntity<>(bytes, getHttpAudioHeaders(), HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @RequestMapping(value = "/{deviceId}/next", method = RequestMethod.GET) + public ResponseEntity getNextTrackId(@PathVariable UUID deviceId) { + UUID trackId = trackService.getNextTrackId(deviceId); + + if (trackId != null) { + return new ResponseEntity<>(trackId, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + private HttpHeaders getHttpAudioHeaders() { + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.add("Content-Type", "audio/mpeg"); + return responseHeaders; + } +} diff --git a/v3/src/main/java/ownradio/web/rest/v3/HistoryController.java b/v3/src/main/java/ownradio/web/rest/v3/HistoryController.java new file mode 100644 index 0000000..383394b --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v3/HistoryController.java @@ -0,0 +1,104 @@ +package ownradio.web.rest.v3; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ownradio.domain.Device; +import ownradio.domain.History; +import ownradio.domain.Track; +import ownradio.repository.DeviceRepository; +import ownradio.repository.TrackRepository; +import ownradio.service.DeviceService; +import ownradio.service.HistoryService; +import ownradio.service.TrackService; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.UUID; + +/** + * Created by a.polunina on 28.11.2016. + */ +@Slf4j +@RestController("HistoryControllerV3") +@RequestMapping("/v3/histories") +public class HistoryController { + private final HistoryService historyService; + private final TrackService trackService; + private final DeviceService deviceService; + + @Autowired + public HistoryController(HistoryService historyService, TrackService trackService, DeviceService deviceService) { + this.historyService = historyService; + this.trackService = trackService; + this.deviceService = deviceService; + } + + @Data + private static class HistoryDTO { + private UUID deviceId; + private UUID trackId; + private String lastListen; + private int isListen; // 1, -1 + private Integer methodid; + + public History getHistory() { + Calendar calendar; + try { + calendar = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'H:m:s"); + calendar.setTime( sdf.parse(lastListen)); + }catch (Exception ex){ + calendar = null; + } + + History history = new History(); + history.setLastListen(calendar); + history.setIsListen(isListen); + history.setMethodid(methodid); + + return history; + } + } + + //form-data + @RequestMapping(value = "/{deviceId}/{trackId}", method = RequestMethod.POST) + public ResponseEntity save(@PathVariable UUID deviceId, @PathVariable UUID trackId, HistoryDTO historyDTO) { + return getResponseEntity(deviceId, trackId, historyDTO.getHistory()); + } + + //json + @RequestMapping(value = "/{deviceId}/{trackId}", method = RequestMethod.POST, headers = "Content-Type=application/json") + public ResponseEntity save2(@PathVariable UUID deviceId, @PathVariable UUID trackId, @RequestBody History history) { + return getResponseEntity(deviceId, trackId, history); + } + + private ResponseEntity getResponseEntity(@PathVariable UUID deviceId, @PathVariable UUID trackId, @RequestBody History history) { + try { + if(deviceService.getById(deviceId) == null || trackService.getById(trackId) == null) + return new ResponseEntity(HttpStatus.OK); + + log.info("deviceId:{} trackId: {}",deviceId.toString(),trackId.toString()); + log.info("{} {} {}",history.getLastListen(), history.getIsListen(), history.getMethodid()); + Track track = trackService.getById(trackId); + Device device = deviceService.getById(deviceId); + + if (track == null || device == null) { + throw new RuntimeException("Track or Device is null"); + } + + history.setTrack(track); + history.setDevice(device); + + historyService.save(history, true); + log.info("Save history, rating and update ratios"); + return new ResponseEntity(HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/v3/src/main/java/ownradio/web/rest/v3/TrackController.java b/v3/src/main/java/ownradio/web/rest/v3/TrackController.java new file mode 100644 index 0000000..13bf78d --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v3/TrackController.java @@ -0,0 +1,159 @@ +package ownradio.web.rest.v3; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import ownradio.domain.Device; +import ownradio.domain.DownloadTrack; +import ownradio.domain.NextTrack; +import ownradio.domain.Track; +import ownradio.repository.DownloadTrackRepository; +import ownradio.repository.TrackRepository; +import ownradio.service.TrackService; +import ownradio.util.ResourceUtil; + +import java.io.File; +import java.util.*; + +/** + * Created by a.polunina on 28.11.2016. + */ +@Slf4j +@RestController("TrackControllerV3") +@RequestMapping(value = "/v3/tracks") +public class TrackController { + + private final TrackService trackService; + private final TrackRepository trackRepository; + private final DownloadTrackRepository downloadTrackRepository; + + @Autowired + public TrackController(TrackService trackService, TrackRepository trackRepository, DownloadTrackRepository downloadTrackRepository) { + this.trackService = trackService; + this.trackRepository = trackRepository; + this.downloadTrackRepository = downloadTrackRepository; + } + + @Data + private static class TrackDTO { + private UUID fileGuid; + private String fileName; + private String filePath; + private UUID deviceId; + private MultipartFile musicFile; + + public Track getTrack() { + Device device = new Device(); + device.setRecid(deviceId); + + Track track = new Track(); + track.setRecid(fileGuid); + track.setDevice(device); + track.setPath("---"); + track.setLocaldevicepathupload(filePath); + + return track; + } + } + + @RequestMapping(method = RequestMethod.POST) + public ResponseEntity save(TrackDTO trackDTO) { + if (trackDTO.getMusicFile().isEmpty()) { + return new ResponseEntity(HttpStatus.BAD_REQUEST); + } + + try { + trackService.save(trackDTO.getTrack(), trackDTO.getMusicFile()); + trackService.setTrackInfo(trackDTO.getTrack().getRecid()); + return new ResponseEntity(HttpStatus.CREATED); + } catch (Exception e) { + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + + } + + @RequestMapping(value = "/{id}", method = RequestMethod.GET) + public ResponseEntity getTrack(@PathVariable UUID id) { + Track track = trackService.getById(id); + + if (track != null) { + byte[] bytes = ResourceUtil.read(track.getPath()); + return new ResponseEntity<>(bytes, getHttpAudioHeaders(), HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + private HttpHeaders getHttpAudioHeaders() { + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.add("Content-Type", "audio/mpeg"); + return responseHeaders; + } + + @RequestMapping(value = "/{deviceId}/next", method = RequestMethod.GET) + public ResponseEntity getNextTrack(@PathVariable UUID deviceId) { + NextTrack nextTrack = null; + try { + nextTrack = trackService.getNextTrackIdV2(deviceId); + }catch (Exception ex){ + + } + + Map trackInfo = new HashMap<>(); + if (nextTrack != null) { + try { + Track track = trackRepository.findOne(nextTrack.getTrackid()); + //Сохраняем информацию об отданном треке +// Device device = new Device(); +// device.setRecid(deviceId); +// DownloadTrack downloadTrack = new DownloadTrack(); +// downloadTrack.setTrack(track); +// downloadTrack.setDevice(device); +// downloadTrack.setMethodid(nextTrack.getMethodid()); +// downloadTrack.setUserrecommend(nextTrack.getUseridrecommended()); +// downloadTrack.setTxtrecommendinfo(nextTrack.getTxtrecommendedinfo()); +// downloadTrackRepository.saveAndFlush(downloadTrack); + + File file = new File(track.getPath()); + if(!file.exists()){ + track.setIsexist(0); + trackRepository.saveAndFlush(track); + return getNextTrack(deviceId); + } + + if(track.getIsfilledinfo() == null || track.getIsfilledinfo() != 1) + trackService.setTrackInfo(track.getRecid()); + + if(track.getIscensorial() != null && track.getIscensorial() == 0) + return getNextTrack(deviceId); + if(track.getLength() < 120) + return getNextTrack(deviceId); + + trackInfo.put("id", nextTrack.getTrackid().toString()); + trackInfo.put("length", String.valueOf(track.getLength())); + if(track.getRecname() != null && !track.getRecname().isEmpty() && !track.getRecname().equals("null")) + trackInfo.put("name", track.getRecname()); + else + trackInfo.put("name", "Unknown track"); + if(track.getArtist() != null && !track.getArtist().isEmpty() && !track.getArtist().equals("null")) + trackInfo.put("artist", track.getArtist()); + else + trackInfo.put("artist", "Unknown artist"); + trackInfo.put("methodid", nextTrack.getMethodid().toString()); + + log.info("getNextTrack return {} {}", nextTrack.getMethodid().toString(), trackInfo.get("id")); + return new ResponseEntity<>(trackInfo, HttpStatus.OK); + }catch (Exception ex){ + log.info("{}", ex.getMessage()); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } +} \ No newline at end of file diff --git a/v3/src/main/java/ownradio/web/rest/v4/DeviceController.java b/v3/src/main/java/ownradio/web/rest/v4/DeviceController.java new file mode 100644 index 0000000..88846b0 --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v4/DeviceController.java @@ -0,0 +1,47 @@ +package ownradio.web.rest.v4; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ownradio.domain.Device; +import ownradio.domain.User; +import ownradio.repository.DeviceRepository; +import ownradio.service.DeviceService; +import ownradio.service.UserService; + +import java.util.UUID; + +/** + * Created by a.polunina on 03.04.2017. + */ +@Slf4j +//@CrossOrigin +@RestController +@RequestMapping("/v4/devices") +public class DeviceController { + private final DeviceRepository deviceRepository; + + @Autowired + public DeviceController(DeviceRepository deviceRepository) { + this.deviceRepository = deviceRepository; + } + + @RequestMapping(value = "/{deviceId}/registerdevice", method = RequestMethod.GET) + public ResponseEntity registerDevice(@PathVariable UUID deviceId){ + return getResponseEntityRegisterDevice(deviceId, null); + } + + @RequestMapping(value = "/{deviceId}/{deviceName}/registerdevice", method = RequestMethod.GET) + public ResponseEntity registerDevice(@PathVariable UUID deviceId, @PathVariable String deviceName){ + return getResponseEntityRegisterDevice(deviceId, deviceName); + } + + private ResponseEntity getResponseEntityRegisterDevice(UUID deviceId, String deviceName) { + if(deviceName == null) + deviceName = "New unknown device"; + deviceRepository.registerdevice(deviceId, deviceName); + return new ResponseEntity(HttpStatus.OK); + } +} diff --git a/v3/src/main/java/ownradio/web/rest/v4/HistoryController.java b/v3/src/main/java/ownradio/web/rest/v4/HistoryController.java new file mode 100644 index 0000000..017a41a --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v4/HistoryController.java @@ -0,0 +1,127 @@ +package ownradio.web.rest.v4; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ownradio.domain.Device; +import ownradio.domain.History; +import ownradio.domain.Track; +import ownradio.service.DeviceService; +import ownradio.service.HistoryService; +import ownradio.service.TrackService; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.UUID; + +/** + * Created by a.polunina on 14.03.2017. + */ +@Slf4j +//@CrossOrigin +@RestController("HistoryControllerV4") +@RequestMapping("/v4/histories") +public class HistoryController { + private final HistoryService historyService; + private final TrackService trackService; + private final DeviceService deviceService; + + @Autowired + public HistoryController(HistoryService historyService, TrackService trackService, DeviceService deviceService) { + this.historyService = historyService; + this.trackService = trackService; + this.deviceService = deviceService; + } + + @Data + private static class HistoryDTO { + private UUID deviceId; + private UUID trackId; + private String lastListen; + private int isListen; // 1, -1 + private Integer methodid; + + public History getHistory() { + Calendar calendar; + try { + calendar = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'H:m:s"); + calendar.setTime( sdf.parse(lastListen)); + }catch (Exception ex){ + calendar = null; + } + + History history = new History(); + history.setLastListen(calendar); + history.setIsListen(isListen); + history.setMethodid(methodid); + + return history; + } + } + + //form-data + @RequestMapping(value = "/{deviceId}/{trackId}", method = RequestMethod.POST) + public ResponseEntity save(@PathVariable UUID deviceId, @PathVariable UUID trackId, HistoryDTO historyDTO) { + return getResponseEntity(deviceId, trackId, historyDTO.getHistory()); + } + + //json + @RequestMapping(value = "/{deviceId}/{trackId}", method = RequestMethod.POST, headers = "Content-Type=application/json") + public ResponseEntity save2(@PathVariable UUID deviceId, @PathVariable UUID trackId, @RequestBody History history) { + return getResponseEntity(deviceId, trackId, history); + } + + private ResponseEntity getResponseEntity(@PathVariable UUID deviceId, @PathVariable UUID trackId, @RequestBody History history) { + try { + if(deviceService.getById(deviceId) == null || trackService.getById(trackId) == null) + return new ResponseEntity(HttpStatus.NOT_FOUND); + + log.info("deviceId:{} trackId: {}",deviceId.toString(),trackId.toString()); + log.info("{} {} {} {}",history.getRecid(), history.getLastListen(), history.getIsListen(), history.getMethodid()); + Track track = trackService.getById(trackId); + Device device = deviceService.getById(deviceId); + + if (track == null || device == null) { + throw new RuntimeException("Track or Device is null"); + } + +// if(historyService.getById(history.getRecid()) == null) { +// History newHistory = history; +// newHistory.setRecid(newHistory.getRecid()); + if(history.getRecid() != null){ + History historyTemp = historyService.getById(history.getRecid()); + if(historyTemp != null){ + historyTemp.setCountsend(((historyTemp.getCountsend()==null?0:historyTemp.getCountsend())) + 1); +// historyTemp.setComment(historyTemp.getComment() + (new Date()).toString() + "; "); + historyService.save(historyTemp, false); + return new ResponseEntity(HttpStatus.ALREADY_REPORTED); + } else { + historyTemp = new History(); + historyTemp.setRecid(history.getRecid()); + historyTemp.setTrack(track); + historyTemp.setDevice(device); + historyTemp.setCountsend(1); + historyTemp.setIsListen(history.getIsListen()); + historyTemp.setLastListen(history.getLastListen()); + historyService.save(historyTemp, true); + } + } else { + history.setTrack(track); + history.setDevice(device); + history.setCountsend(1); + historyService.save(history, true); + } + log.info("Save history, rating and update ratios"); + //} + return new ResponseEntity(HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + +} \ No newline at end of file diff --git a/v3/src/main/java/ownradio/web/rest/v4/StatisticsController.java b/v3/src/main/java/ownradio/web/rest/v4/StatisticsController.java new file mode 100644 index 0000000..be3780a --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v4/StatisticsController.java @@ -0,0 +1,109 @@ +package ownradio.web.rest.v4; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ownradio.domain.Device; +import ownradio.domain.DownloadTrack; +import ownradio.domain.TracksHistory; +import ownradio.domain.UsersRating; +import ownradio.repository.DeviceRepository; +import ownradio.service.DeviceService; +import ownradio.service.DownloadTrackService; +import ownradio.service.UserService; + +import java.util.List; +import java.util.UUID; + +/** + * Created by a.polunina on 17.03.2017. + */ +@Slf4j +//@CrossOrigin +@RestController +@RequestMapping("/v4/statistics") +public class StatisticsController { + + private final UserService userService; + private final DeviceService deviceService; + private final DownloadTrackService downloadTrackService; + + @Autowired + public StatisticsController(UserService userService, DeviceService deviceService, DownloadTrackService downloadTrackService){ + this.userService = userService; + this.deviceService = deviceService; + this.downloadTrackService = downloadTrackService; + + } + + @RequestMapping(value = "/{userId}/getuserdevices", method = RequestMethod.GET) + public ResponseEntity getUserDevices(@PathVariable UUID userId) { + try { + List devices = deviceService.getByUserid(userId); + + return new ResponseEntity<>(devices, HttpStatus.OK); + }catch (Exception ex){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + + @RequestMapping(value = "/{deviceId}/{countTracks}/getlasttracks", method = RequestMethod.GET) + public ResponseEntity getLastTracksByDevice(@PathVariable UUID deviceId, @PathVariable Integer countTracks) { + try { + List downloadedTracks = downloadTrackService.getLastTracksByDevice(deviceId, countTracks); +// Device device = new Device(); +// device.setRecid(deviceId); +// List downloadedTracks = downloadTrackRepository.findFirst3ByDeviceOrderByReccreatedDesc(device); + + return new ResponseEntity<>(downloadedTracks, HttpStatus.OK); + }catch (Exception ex){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + + @RequestMapping(value = "/usersrating/{countRows}", method = RequestMethod.GET) + public ResponseEntity getUsersRating(@PathVariable Integer countRows) { + try { + List usersRating = userService.getUsersRating(countRows); + + return new ResponseEntity<>(usersRating, HttpStatus.OK); + }catch (Exception ex){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @RequestMapping(value = "/{deviceId}/{countTracks}/gettrackshistorybydevice", method = RequestMethod.GET) + public ResponseEntity getTracksHistoryByDevice(@PathVariable UUID deviceId, @PathVariable Integer countTracks) { + try { + List tracksHistories = downloadTrackService.getTracksHistoryByDevice(deviceId, countTracks); + return new ResponseEntity<>(tracksHistories, HttpStatus.OK); + }catch (Exception ex){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @RequestMapping(value = "/getlastdevices", method = RequestMethod.GET) + public ResponseEntity getLastDevices() { + try{ + List lastActiveDevices = deviceService.getLastDevices(); + return new ResponseEntity<>(lastActiveDevices, HttpStatus.OK); + }catch (Exception ex){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @RequestMapping(value = "/getlastusers/{countUsers}", method = RequestMethod.GET) + public ResponseEntity getLastUsers(@PathVariable Integer countUsers) { + try{ + List lastActiveDevices = userService.getLastUsers(countUsers); + return new ResponseEntity<>(lastActiveDevices, HttpStatus.OK); + }catch (Exception ex){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } +} + diff --git a/v3/src/main/java/ownradio/web/rest/v4/TrackController.java b/v3/src/main/java/ownradio/web/rest/v4/TrackController.java new file mode 100644 index 0000000..d999e7a --- /dev/null +++ b/v3/src/main/java/ownradio/web/rest/v4/TrackController.java @@ -0,0 +1,156 @@ +package ownradio.web.rest.v4; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import ownradio.domain.Device; +import ownradio.domain.NextTrack; +import ownradio.domain.Track; +import ownradio.repository.DownloadTrackRepository; +import ownradio.repository.TrackRepository; +import ownradio.service.TrackService; +import ownradio.util.ResourceUtil; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Created by a.polunina on 14.03.2017. + */ +@Slf4j +//@CrossOrigin +@RestController("TrackControllerV4") +@RequestMapping(value = "/v4/tracks") +public class TrackController { + + private final TrackService trackService; + private final TrackRepository trackRepository; + private final DownloadTrackRepository downloadTrackRepository; + + @Autowired + public TrackController(TrackService trackService, TrackRepository trackRepository, DownloadTrackRepository downloadTrackRepository) { + this.trackService = trackService; + this.trackRepository = trackRepository; + this.downloadTrackRepository = downloadTrackRepository; + } + + @Data + private static class TrackDTO { + private UUID fileGuid; + private String fileName; + private String filePath; + private UUID deviceId; + private MultipartFile musicFile; + + public Track getTrack() { + Device device = new Device(); + device.setRecid(deviceId); + + Track track = new Track(); + track.setRecid(fileGuid); + track.setDevice(device); + track.setPath("---"); + track.setLocaldevicepathupload(filePath); + + return track; + } + } + + @RequestMapping(method = RequestMethod.POST) + public ResponseEntity save(TrackDTO trackDTO) { + if (trackDTO.getMusicFile().isEmpty()) { + return new ResponseEntity(HttpStatus.BAD_REQUEST); + } + + try { + trackService.save(trackDTO.getTrack(), trackDTO.getMusicFile()); + trackService.setTrackInfo(trackDTO.getTrack().getRecid()); + return new ResponseEntity(HttpStatus.CREATED); + } catch (Exception e) { + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + + } + + @RequestMapping(value = "/{id}", method = RequestMethod.GET) + public ResponseEntity getTrack(@PathVariable UUID id) { + Track track = trackService.getById(id); + + if (track != null) { + byte[] bytes = ResourceUtil.read(track.getPath()); + return new ResponseEntity<>(bytes, getHttpAudioHeaders(), HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + private HttpHeaders getHttpAudioHeaders() { + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.add("Content-Type", "audio/mpeg"); + return responseHeaders; + } + + @RequestMapping(value = "/{deviceId}/next", method = RequestMethod.GET) + public ResponseEntity getNextTrack(@PathVariable UUID deviceId) { + NextTrack nextTrack = null; + try { + nextTrack = trackService.getNextTrackIdV2(deviceId); + }catch (Exception ex){ + log.info("{}", ex.getMessage()); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + + Map trackInfo = new HashMap<>(); + if (nextTrack != null) { + try { + Track track = trackRepository.findOne(nextTrack.getTrackid()); + + File file = new File(track.getPath()); + if(!file.exists()){ + track.setIsexist(0); + trackRepository.saveAndFlush(track); + return getNextTrack(deviceId); + } + + if(track.getIsfilledinfo() == null || track.getIsfilledinfo() != 1) + trackService.setTrackInfo(track.getRecid()); + + if(track.getIscensorial() != null && track.getIscensorial() == 0) + return getNextTrack(deviceId); + if(track.getLength() != null && track.getLength() < 120) + return getNextTrack(deviceId); + + trackInfo.put("id", nextTrack.getTrackid().toString()); + trackInfo.put("length", String.valueOf(track.getLength())); + if(track.getRecname() != null && !track.getRecname().isEmpty() && !track.getRecname().equals("null")) + trackInfo.put("name", track.getRecname()); + else + trackInfo.put("name", "Unknown track"); + if(track.getArtist() != null && !track.getArtist().isEmpty() && !track.getArtist().equals("null")) + trackInfo.put("artist", track.getArtist()); + else + trackInfo.put("artist", "Unknown artist"); + trackInfo.put("pathupload", track.getLocaldevicepathupload()); + + trackInfo.put("timeexecute", nextTrack.getTimeexecute()); + + + log.info("getNextTrack return {} {}", nextTrack.getMethodid(), trackInfo.get("id")); + return new ResponseEntity<>(trackInfo, HttpStatus.OK); + }catch (Exception ex){ + log.info("{}", ex.getMessage()); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } +} diff --git a/v3/src/main/resources/application-dev.properties b/v3/src/main/resources/application-dev.properties new file mode 100644 index 0000000..5814481 --- /dev/null +++ b/v3/src/main/resources/application-dev.properties @@ -0,0 +1,17 @@ +this-url:http://${server.address:localhost}:${server.port:8080} +spring.h2.console.enabled=true +spring.datasource.url=jdbc:h2:mem:.;MODE=PostgreSQL +spring.datasource.username=sa +spring.datasource.password= +spring.datasource.platform=org.hibernate.dialect.H2Dialect +spring.http.multipart.max-file-size=256Mb +spring.http.multipart.max-request-size=256Mb +#spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.show-sql=true +spring.jpa.hibernate.ddl-auto=update +logging.file=logs/app.log +logging.level.ownradio=debug +logging.level.org.springframework=warn +logging.level.org.hibernate=warn + diff --git a/v3/src/main/resources/application-prod.properties b/v3/src/main/resources/application-prod.properties new file mode 100644 index 0000000..ce125c6 --- /dev/null +++ b/v3/src/main/resources/application-prod.properties @@ -0,0 +1,26 @@ +#spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/ownradio +#spring.datasource.username=tanat +#spring.datasource.password=123456 +spring.datasource.url=jdbc:postgresql://10.10.0.67:5432/ownRadioJavaTest +spring.datasource.username=a.polunina +spring.datasource.password=6ImtqMGAt1 +spring.datasource.platform=org.hibernate.dialect.PostgreSQL94Dialect +spring.datasource.test-while-idle=true +spring.datasource.test-on-borrow=true +spring.datasource.validation-query=SELECT 1 +spring.datasource.time-between-eviction-runs-millis=5000 +spring.datasource.min-evictable-idle-time-millis=60000 +spring.http.multipart.max-file-size=256Mb +spring.http.multipart.max-request-size=256Mb +#spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.show-sql=true +spring.jpa.hibernate.ddl-auto=update +logging.file=logs/app.log +logging.level.ownradio=info +logging.level.org.springframework=warn +logging.level.org.hibernate=warn +#server.port = 9090 +#There is switch on file with sql scripts for auto create stored procedures +#spring.datasource.schema=classpath:/data/postgresql/schema.sql +#spring.datasource.data=classpath:/data/postgresql/data.sql \ No newline at end of file diff --git a/v3/src/main/resources/data/h2/schema.sql b/v3/src/main/resources/data/h2/schema.sql new file mode 100644 index 0000000..e69de29 diff --git a/v3/src/main/resources/data/postgresql/schema.sql b/v3/src/main/resources/data/postgresql/schema.sql new file mode 100644 index 0000000..3a245d3 --- /dev/null +++ b/v3/src/main/resources/data/postgresql/schema.sql @@ -0,0 +1,984 @@ +CREATE OR REPLACE FUNCTION getnexttrackid(IN i_deviceid UUID) + RETURNS SETOF UUID AS +' +DECLARE + i_userid UUID = i_deviceid; +BEGIN + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + ''New user recname'', + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_userid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + ''New device recname'', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + + RETURN QUERY + SELECT tracks.recid + FROM tracks + LEFT JOIN + ratings + ON tracks.recid = ratings.trackid AND ratings.userid = i_userid + WHERE ratings.ratingsum >= 0 OR ratings.ratingsum IS NULL + ORDER BY RANDOM() + LIMIT 1; +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION getnexttrackid_string(i_deviceid UUID) + RETURNS SETOF CHARACTER VARYING AS +' +BEGIN + RETURN QUERY SELECT CAST(getnexttrackid(i_deviceid) AS CHARACTER VARYING); +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION registertrack( + i_trackid UUID, + i_localdevicepathupload CHARACTER VARYING, + i_path CHARACTER VARYING, + i_deviceid UUID) + RETURNS BOOLEAN AS +' +DECLARE + i_userid UUID = i_deviceid; + i_historyid UUID; + i_ratingid UUID; +BEGIN + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + SELECT uuid_generate_v4() + INTO i_historyid; + SELECT uuid_generate_v4() + INTO i_ratingid; + + -- + -- Функция добавляет запись о треке в таблицу треков и делает сопутствующие записи в + -- таблицу статистики прослушивания и рейтингов. Если пользователя, загружающего трек + -- нет в базе, то он добавляется в таблицу пользователей. + -- + + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + ''New user recname'', + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_userid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + ''New device recname'', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + + -- Добавляем трек в базу данных + INSERT INTO tracks (recid, localdevicepathupload, path, deviceid, reccreated, iscensorial, isexist) + VALUES (i_trackid, i_localdevicepathupload, i_path, i_deviceid, now(), 2, 1); + + -- Добавляем запись о прослушивании трека в таблицу истории прослушивания + INSERT INTO histories (recid, deviceid, trackid, isListen, lastListen, methodid, reccreated) + VALUES (i_historyid, i_deviceid, i_trackid, 1, now(), 2, now()); + + -- Добавляем запись в таблицу рейтингов + INSERT INTO ratings (recid, userid, trackid, lastListen, ratingsum, reccreated) + VALUES (i_ratingid, i_userid, i_trackid, now(), 1, now()); + + RETURN TRUE; +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION getnexttrackid_v2(i_deviceid UUID) + RETURNS TABLE(track UUID, methodid INTEGER) +AS +' +DECLARE + i_userid UUID = i_deviceid; + rnd INTEGER = (SELECT trunc(random() * 10)); -- получаем случайное число от 0 до 9 + o_methodid INTEGER; -- id метода выбора трека +BEGIN + + -- Выбираем следующий трек + + -- В 9/10 случаях выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + IF (rnd > 1) + THEN + o_methodid = 2; + RETURN QUERY + SELECT + trackid, + o_methodid + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - INTERVAL ''1 day'' + AND ratingsum >= 0 + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + END IF; + + -- В 1/10 случае выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + AND isexist = 1 + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; + RETURN QUERY + SELECT + recid, + o_methodid + FROM tracks + WHERE isexist = 1 + ORDER BY RANDOM() + LIMIT 1; + RETURN; +END; +' +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION getnexttrack(i_deviceid UUID) + RETURNS TABLE( + track CHARACTER VARYING + , methodid INTEGER + , useridrecommended CHARACTER VARYING + , txtrecommendedinfo CHARACTER VARYING) +AS +' +DECLARE + i_userid UUID = i_deviceid; -- в дальнейшем заменить получением userid по deviceid +BEGIN + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + ''New user recname'', + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_userid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + ''New device recname'', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + + -- Возвращаем trackid, конвертируя его в character varying и methodid + RETURN QUERY SELECT + CAST((nexttrack.track) AS CHARACTER VARYING), + nexttrack.methodid, + CAST((nexttrack.useridrecommended) AS CHARACTER VARYING), + nexttrack.txtrecommendedinfo + FROM getnexttrackid_v10(i_deviceid) AS nexttrack; +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION calculateratios() + RETURNS boolean AS +' +DECLARE +-- объявляем курсор и запрос для него + curs1 CURSOR FOR SELECT * FROM( + -- рассчитываем матрицу коэффициентов схожести интересов для каждой пары пользователей + SELECT r.userid as userid01, r2.userid as userid02, SUM(r.ratingsum * r2.ratingsum) as s + FROM ratings r + INNER JOIN ratings r2 ON r.trackid = r2.trackid + AND r.userid != r2.userid + GROUP BY r.userid, r2.userid + ) AS cursor1; + cuser1 uuid; + cuser2 uuid; + cratio integer; +BEGIN + DROP TABLE IF EXISTS temp_ratio; + CREATE TEMP TABLE temp_ratio(userid1 uuid, userid2 uuid, ratio integer); + + OPEN curs1; -- открываем курсор + LOOP -- в цикле проходим по строкам результата запроса курсора + FETCH curs1 INTO cuser1, cuser2, cratio; + + IF NOT FOUND THEN EXIT; -- если данных нет - выходим + END IF; + -- если для данной пары пользователей уже записан коэффициент - пропускаем, иначе - записываем во временную таблицу + IF NOT EXISTS (SELECT * FROM temp_ratio WHERE userid1 = cuser2 AND userid2 = cuser1 OR userid1 = cuser1 AND userid2 = cuser2) THEN + INSERT INTO temp_ratio(userid1, userid2, ratio) + VALUES (cuser1, cuser2, cratio); + END IF; + END LOOP; + CLOSE curs1; -- закрываем курсор + + -- обновляем имеющиеся коэффициенты в таблице ratios + UPDATE ratios SET ratio = temp_ratio.ratio, recupdated = now() FROM temp_ratio + WHERE (ratios.userid1 = temp_ratio.userid1 AND ratios.userid2 = temp_ratio.userid2) + OR (ratios.userid1 = temp_ratio.userid2 AND ratios.userid2 = temp_ratio.userid1); + + -- если в ratios меньше пар пользователей, чем во временной таблице - вставляем недостающие записи + IF (SELECT COUNT(*) FROM ratios) < (SELECT COUNT(*) FROM temp_ratio) THEN + INSERT INTO ratios (userid1, userid2, ratio, reccreated) + (SELECT tr.userid1, tr.userid2, tr.ratio, now() FROM temp_ratio AS tr + LEFT OUTER JOIN ratios AS rr ON tr.userid1 = rr.userid1 AND tr.userid2 = rr.userid2 OR tr.userid1 = rr.userid2 AND tr.userid2 = rr.userid1 + WHERE rr.userid1 IS NULL OR rr.userid2 IS NULL + ); + END IF; + RETURN TRUE; +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION updateratios(i_userid uuid) + RETURNS boolean AS +' +-- Функция обновляет таблицу коэффициентов схожести интересов для выбранного пользователя +DECLARE + -- объявляем курсор и запрос для него + curs1 CURSOR FOR SELECT * FROM( + -- рассчитываем матрицу коэффициентов схожести интересов для каждой пары пользователей + SELECT r.userid as userid01, r2.userid as userid02, SUM(r.ratingsum * r2.ratingsum) as s + FROM ratings r + INNER JOIN ratings r2 ON r.trackid = r2.trackid + AND r.userid != r2.userid + AND (r.userid = i_userid OR r2.userid = i_userid) + GROUP BY r.userid, r2.userid + ) AS cursor1; + cuser1 uuid; + cuser2 uuid; + cratio integer; +BEGIN + DROP TABLE IF EXISTS temp_ratio; + CREATE TEMP TABLE temp_ratio(userid1 uuid, userid2 uuid, ratio integer); + + OPEN curs1; -- открываем курсор + LOOP -- в цикле проходим по строкам результата запроса курсора + FETCH curs1 INTO cuser1, cuser2, cratio; + + IF NOT FOUND THEN EXIT; -- если данных нет - выходим + END IF; + + INSERT INTO temp_ratio(userid1, userid2, ratio) + VALUES (cuser1, cuser2, cratio); + + END LOOP; + CLOSE curs1; -- закрываем курсор + + UPDATE ratios SET ratio = temp_ratio.ratio, recupdated = now() FROM temp_ratio + WHERE ratios.userid1 = temp_ratio.userid1 AND ratios.userid2 = temp_ratio.userid2; + + + INSERT INTO ratios (userid1, userid2, ratio, reccreated) + (SELECT temp_ratio.userid1,temp_ratio.userid2, temp_ratio.ratio, now() + FROM temp_ratio + LEFT OUTER JOIN ratios ON + temp_ratio.userid1 = ratios.userid1 AND temp_ratio.userid2 = ratios.userid2 + WHERE ratios.userid1 IS NULL OR ratios.userid2 IS NULL + ); + +RETURN TRUE; +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION getnexttrackid_v8(IN i_deviceid uuid) + RETURNS TABLE(track uuid, methodid integer, + useridrecommended uuid + , txtrecommendedinfo character varying + ) AS +' +DECLARE + i_userid UUID = i_deviceid; + rnd INTEGER = (SELECT trunc(random() * 1001)); + o_methodid INTEGER; -- id метода выбора трека + owntracks INTEGER; -- количество "своих" треков пользователя (обрезаем на 900 шт) + arrusers uuid ARRAY; -- массив пользователей для i_userid с неотрицательнымм коэффициентами схожести интересов + exceptusers uuid ARRAY; -- массив пользователей для i_userid с котороми не было пересечений по трекам +BEGIN + -- Выбираем следующий трек + + -- Определяем количество "своих" треков пользователя, ограничивая его 900 + owntracks = (SELECT COUNT(*) + FROM ( + SELECT * + FROM ratings + WHERE userid = i_userid + AND ratingsum >= 0 + LIMIT 900) AS count); + + -- Если rnd меньше количества "своих" треков, выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + + IF (rnd < owntracks) + THEN + o_methodid = 2; -- метод выбора из своих треков + RETURN QUERY + SELECT + trackid, + o_methodid, + (SELECT CAST((null) AS UUID)), + (SELECT CAST((null) AS CHARACTER VARYING)) + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - INTERVAL ''1 day'' + AND ratingsum >= 0 + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL ''1 day'') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + END IF; + + -- Если rnd больше количества "своих" треков - рекомендуем трек из треков пользователя с наибольшим + -- коэффициентом схожести интересов и наибольшим рейтингом прослушивания + + -- Выберем всех пользователей с неотрицательным коэффициентом схожести интересов для i_userid + -- отсортировав по убыванию коэффициентов + arrusers = (SELECT ARRAY (SELECT CASE WHEN userid1 = i_userid THEN userid2 + WHEN userid2 = i_userid THEN userid1 + ELSE NULL + END + FROM ratios + WHERE (userid1 = i_userid OR userid2 = i_userid) AND ratio >= 0 + ORDER BY ratio DESC + )); + -- Выбираем пользователя i, с которым у него максимальный коэффициент. Среди его треков ищем трек + -- с максимальным рейтингом прослушивания, за исключением уже прослушанных пользователем i_userid. + -- Если рекомендовать нечего - берем следующего пользователя с наибольшим коэффициентом из оставшихся. + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(arrusers)) LOOP + o_methodid = 4; -- метод выбора из рекомендованных треков + RETURN QUERY + SELECT + trackid, + o_methodid, + arrusers[i], + (SELECT CAST((ratio) AS CHARACTER VARYING) + FROM ratios + WHERE userid1 = i_userid AND userid2 = arrusers[i] OR userid2 = i_userid AND userid1 = arrusers[i] LIMIT 1) + FROM ratings + WHERE userid = arrusers[i] + AND ratingsum > 0 + AND trackid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE deviceid = i_deviceid + AND reccreated > localtimestamp - INTERVAL ''1 day'') + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + ORDER BY ratingsum DESC, RANDOM() + LIMIT 1; + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + RETURN; + END IF; + END LOOP; + -- При отсутствии рекомендаций, выдавать случайный трек из непрослушанных треков с неотрицательным + -- рейтингом среди пользователей с которыми не было пересечений по трекам. + exceptusers = (SELECT ARRAY ( + SELECT * FROM ( + SELECT recid FROM users WHERE recid != i_userid + EXCEPT + (SELECT CASE WHEN userid1 = i_userid THEN userid2 + WHEN userid2 = i_userid THEN userid1 + ELSE NULL + END + FROM ratios WHERE userid1 = i_userid OR userid2 = i_userid) + ) AS us + ORDER BY RANDOM() + ) + ); + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(exceptusers)) LOOP + o_methodid = 6; -- метод выбора из непрослушанных треков с неотрицательным рейтингом среди пользователей с которыми не было пересечений + RETURN QUERY + SELECT + recid, + o_methodid, + exceptusers[i], + (SELECT CAST((''рекомендовано от пользователя с которым не было пересечений'') AS CHARACTER VARYING)) + FROM tracks + WHERE recid IN (SELECT trackid FROM ratings WHERE userid = exceptusers[i] AND ratingsum >= 0) + AND recid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL ''1 day'') + ORDER BY RANDOM() + LIMIT 1; + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + RETURN; + ELSE + + END IF; + END LOOP; + + -- Если таких треков нет - выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; -- метод выбора из непрослушанных треков + RETURN QUERY + SELECT + recid, + o_methodid, + (SELECT CAST((null) AS UUID)), + (SELECT CAST((null) AS CHARACTER VARYING)) + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL ''1 day'') + ORDER BY RANDOM() + LIMIT 1; + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND + THEN RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; -- метод выбора случайного трека + RETURN QUERY + SELECT + recid, + o_methodid, + (SELECT CAST((null) AS UUID)), + (SELECT CAST((null) AS CHARACTER VARYING)) + FROM tracks + WHERE isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + ORDER BY RANDOM() + LIMIT 1; + RETURN; +END; +' +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION getusersrating(IN i_count integer) + RETURNS TABLE(tuserid character varying, treccreated character varying, trecname character varying, trecupdated character varying, towntracks bigint, tlasttracks bigint) AS +' + +BEGIN + IF i_count < 0 THEN + i_count = null; + END IF; +RETURN QUERY SELECT CAST((res1.recid) AS CHARACTER VARYING), CAST((res1.reccreated) AS CHARACTER VARYING), res1.recname, CAST((res1.recupdated) AS CHARACTER VARYING), res1.owntracks, COUNT(res2.userid) AS downloadtracks +FROM + (SELECT u.recid, u.reccreated, u.recname, u.recupdated, COUNT(r.recid) AS owntracks + FROM users u + LEFT OUTER JOIN ratings r ON u.recid = r.userid + GROUP BY u.recid) res1 + LEFT OUTER JOIN (SELECT d.reccreated, dev.userid FROM downloadtracks d + INNER JOIN devices dev + ON dev.recid= d.deviceid AND d.reccreated > localtimestamp - INTERVAL ''1 day'') res2 + ON res2.userid = res1.recid + GROUP BY res1.recid, res1.reccreated, res1.recname, res1.recupdated, res1.owntracks + ORDER BY downloadtracks DESC, owntracks DESC + LIMIT i_count; + +END; +' +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION getlasttracks( + IN i_deviceid uuid, + IN i_count integer) + RETURNS TABLE(recid uuid, reccreated timestamp without time zone, recname character varying, recupdated timestamp without time zone, deviceid uuid, trackid uuid, methodid integer, txtrecommendinfo character varying, userrecommend uuid) AS +' +BEGIN + IF i_count < 0 THEN + i_count = null; + END IF; +RETURN QUERY SELECT * + FROM downloadtracks + WHERE downloadtracks.deviceid = i_deviceid + ORDER BY downloadtracks.reccreated DESC + LIMIT i_count; +END; +' +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION getuserdevices(IN i_userid uuid) + RETURNS TABLE(recid uuid, reccreated timestamp without time zone, recname character varying, recupdated timestamp without time zone, userid uuid) AS +' +BEGIN + RETURN QUERY + SELECT * FROM devices WHERE devices.userid = i_userid; +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION getnexttrackid_v10(IN i_deviceid uuid) + RETURNS TABLE(track uuid, methodid integer, useridrecommended uuid, txtrecommendedinfo character varying) AS +' +DECLARE + i_userid UUID = i_deviceid; --пока не реалезовано объединение пользователей - гуиды одинаковые + rnd INTEGER = (SELECT trunc(random() * 1001)); -- генерируем случайное целое число в диапазоне от 1 до 1000 + o_methodid INTEGER; -- id метода выбора трека + owntracks INTEGER; -- количество "своих" треков пользователя (обрезаем на 900 шт) + arrusers uuid ARRAY; -- массив пользователей для i_userid с неотрицательнымм коэффициентами схожести интересов + exceptusers uuid ARRAY; -- массив пользователей для i_userid с котороми не было пересечений по трекам +BEGIN + DROP TABLE IF EXISTS temp_track; + CREATE TEMP TABLE temp_track(track uuid, methodid integer, useridrecommended uuid, txtrecommendedinfo character varying); + + -- Выбираем следующий трек + + -- Определяем количество "своих" треков пользователя, ограничивая его 900 + owntracks = (SELECT COUNT(*) + FROM ( + SELECT * + FROM ratings + WHERE userid = i_userid + AND ratingsum >= 0 + LIMIT 900) AS count); + + -- Если rnd меньше количества "своих" треков, выбираем трек из треков пользователя (добавленных им или прослушанных до конца) + -- с положительным рейтингом, за исключением прослушанных за последние сутки + + IF (rnd < owntracks) + THEN + o_methodid = 2; -- метод выбора из своих треков + INSERT INTO temp_track ( + SELECT + trackid, + o_methodid, + (SELECT CAST((null) AS UUID)), + (SELECT CAST((null) AS CHARACTER VARYING)) + FROM ratings + WHERE userid = i_userid + AND lastlisten < localtimestamp - INTERVAL ''1 day'' + AND ratingsum >= 0 + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL ''1 day'') + ORDER BY RANDOM() + LIMIT 1); + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND THEN + INSERT INTO downloadtracks (SELECT uuid_generate_v4(),now(),null, null, i_userid, temp_track.track AS trackid, temp_track.methodid AS methodid, temp_track.txtrecommendedinfo AS txtrecommendinfo, temp_track.useridrecommended AS userrecommend FROM temp_track); + RETURN QUERY SELECT * FROM temp_track; + RETURN; + END IF; + END IF; + + -- Если rnd больше количества "своих" треков - рекомендуем трек из треков пользователя с наибольшим + -- коэффициентом схожести интересов и наибольшим рейтингом прослушивания + + -- Выберем всех пользователей с неотрицательным коэффициентом схожести интересов для i_userid + -- отсортировав по убыванию коэффициентов + arrusers = (SELECT ARRAY (SELECT CASE WHEN userid1 = i_userid THEN userid2 + WHEN userid2 = i_userid THEN userid1 + ELSE NULL + END + FROM ratios + WHERE (userid1 = i_userid OR userid2 = i_userid) AND ratio >= 0 + ORDER BY ratio DESC + )); + -- Выбираем пользователя i, с которым у него максимальный коэффициент. Среди его треков ищем трек + -- с максимальным рейтингом прослушивания, за исключением уже прослушанных пользователем i_userid. + -- Если рекомендовать нечего - берем следующего пользователя с наибольшим коэффициентом из оставшихся. + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(arrusers)) LOOP + o_methodid = 4; -- метод выбора из рекомендованных треков + INSERT INTO temp_track ( + SELECT + trackid, + o_methodid, + arrusers[i], + (SELECT CAST ((concat(''Коэффициент схожести '', ratio)) AS CHARACTER VARYING) + FROM ratios + WHERE userid1 = i_userid AND userid2 = arrusers[i] OR userid2 = i_userid AND userid1 = arrusers[i] LIMIT 1) + FROM ratings + WHERE userid = arrusers[i] + AND ratingsum > 0 + AND trackid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND trackid NOT IN (SELECT trackid + FROM downloadtracks + WHERE deviceid = i_deviceid + AND reccreated > localtimestamp - INTERVAL ''1 day'') + AND (SELECT isexist + FROM tracks + WHERE recid = trackid) = 1 + AND ((SELECT length + FROM tracks + WHERE recid = trackid) >= 120 + OR (SELECT length + FROM tracks + WHERE recid = trackid) IS NULL) + AND ((SELECT iscensorial + FROM tracks + WHERE recid = trackid) IS NULL + OR (SELECT iscensorial + FROM tracks + WHERE recid = trackid) != 0) + ORDER BY ratingsum DESC, RANDOM() + LIMIT 1); + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + INSERT INTO downloadtracks (SELECT uuid_generate_v4(),now(),null, null, i_userid, temp_track.track AS trackid, temp_track.methodid AS methodid, temp_track.txtrecommendedinfo AS txtrecommendinfo, temp_track.useridrecommended AS userrecommend FROM temp_track); + RETURN QUERY SELECT * FROM temp_track; + RETURN; + END IF; + END LOOP; + -- При отсутствии рекомендаций, выдавать случайный трек из непрослушанных треков с неотрицательным + -- рейтингом среди пользователей с которыми не было пересечений по трекам. + exceptusers = (SELECT ARRAY ( + SELECT * FROM ( + SELECT recid FROM users WHERE recid != i_userid + EXCEPT + (SELECT CASE WHEN userid1 = i_userid THEN userid2 + WHEN userid2 = i_userid THEN userid1 + ELSE NULL + END + FROM ratios WHERE userid1 = i_userid OR userid2 = i_userid) + ) AS us + ORDER BY RANDOM() + ) + ); + FOR i IN 1.. (SELECT COUNT (*) FROM unnest(exceptusers)) LOOP + o_methodid = 6; -- метод выбора из непрослушанных треков с неотрицательным рейтингом среди пользователей с которыми не было пересечений + INSERT INTO temp_track ( + SELECT + recid, + o_methodid, + exceptusers[i], + (SELECT CAST((''рекомендовано от пользователя с которым не было пересечений'') AS CHARACTER VARYING)) + FROM tracks + WHERE recid IN (SELECT trackid FROM ratings WHERE userid = exceptusers[i] AND ratingsum >= 0) + AND recid NOT IN (SELECT trackid FROM ratings WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL ''1 day'') + ORDER BY RANDOM() + LIMIT 1); + -- Если нашли что рекомендовать - выходим из функции + IF found THEN + INSERT INTO downloadtracks (SELECT uuid_generate_v4(),now(),null, null, i_userid, temp_track.track AS trackid, temp_track.methodid AS methodid, temp_track.txtrecommendedinfo AS txtrecommendinfo, temp_track.useridrecommended AS userrecommend FROM temp_track); + RETURN QUERY SELECT * FROM temp_track; + RETURN; + ELSE + + END IF; + END LOOP; + + -- Если таких треков нет - выбираем случайный трек из ни разу не прослушанных пользователем треков + o_methodid = 3; -- метод выбора из непрослушанных треков + INSERT INTO temp_track ( + SELECT + recid, + o_methodid, + (SELECT CAST((null) AS UUID)), + (SELECT CAST((null) AS CHARACTER VARYING)) + FROM tracks + WHERE recid NOT IN + (SELECT trackid + FROM ratings + WHERE userid = i_userid) + AND isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + AND recid NOT IN (SELECT trackid + FROM downloadtracks + WHERE reccreated > localtimestamp - INTERVAL ''1 day'') + ORDER BY RANDOM() + LIMIT 1); + + -- Если такой трек найден - выход из функции, возврат найденного значения + IF FOUND THEN + INSERT INTO downloadtracks (SELECT uuid_generate_v4(),now(),null, null, i_userid, temp_track.track AS trackid, temp_track.methodid AS methodid, temp_track.txtrecommendedinfo AS txtrecommendinfo, temp_track.useridrecommended AS userrecommend FROM temp_track); + RETURN QUERY SELECT * FROM temp_track; + RETURN; + END IF; + + -- Если предыдущие запросы вернули null, выбираем случайный трек + o_methodid = 1; -- метод выбора случайного трека + INSERT INTO temp_track ( + SELECT + recid, + o_methodid, + (SELECT CAST((null) AS UUID)), + (SELECT CAST((null) AS CHARACTER VARYING)) + FROM tracks + WHERE isexist = 1 + AND (iscensorial IS NULL OR iscensorial != 0) + AND (length > 120 OR length IS NULL) + ORDER BY RANDOM() + LIMIT 1); + INSERT INTO downloadtracks (SELECT uuid_generate_v4(),now(),null, null, i_userid, temp_track.track AS trackid, temp_track.methodid AS methodid, temp_track.txtrecommendedinfo AS txtrecommendinfo, temp_track.useridrecommended AS userrecommend FROM temp_track); + RETURN QUERY SELECT * FROM temp_track; + RETURN; +END; +' +LANGUAGE plpgsql; + + + +CREATE OR REPLACE FUNCTION registerdevice( + i_deviceid uuid, + i_devicename character varying) + RETURNS boolean AS +' +BEGIN + -- Функция регистрации нового устройства + + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_deviceid, + i_devicename, + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_deviceid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_deviceid, + i_devicename, + now(); + END IF; + RETURN TRUE; +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION getlastdevices() + RETURNS TABLE(recid character varying) AS +' +BEGIN + + RETURN QUERY SELECT CAST((dev.recid) AS CHARACTER VARYING) + FROM devices dev + INNER JOIN downloadtracks down + ON dev.recid = down.deviceid + GROUP BY dev.recid + ORDER BY MAX(down.reccreated) DESC + LIMIT 100; + +END; +' +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION gettrackshistorybydevice( + IN i_deviceid uuid, + IN i_count integer) + RETURNS TABLE(downloadtrackrecid character varying, historyrecid character varying) AS +' +BEGIN + IF i_count < 0 THEN + i_count = null; + END IF; + RETURN QUERY SELECT CAST((d.recid) AS CHARACTER VARYING), CAST((h.recid) AS CHARACTER VARYING) + FROM downloadtracks d + LEFT OUTER JOIN histories h + ON h.deviceid = d.deviceid AND h.trackid = d.trackid + WHERE d.deviceid = i_deviceid + ORDER BY d.reccreated DESC, h.reccreated DESC, h.lastlisten DESC + LIMIT i_count; +END; +' +LANGUAGE plpgsql; + + + +DROP FUNCTION getnexttrack_v2(uuid); + +CREATE OR REPLACE FUNCTION getnexttrack_v2(IN i_deviceid uuid) + RETURNS TABLE(track character varying, method integer, useridrecommended character varying, txtrecommendedinfo character varying, timeexecute character varying) AS +' +DECLARE + declare t timestamptz := clock_timestamp(); -- запоминаем начальное время выполнения процедуры + i_userid UUID = i_deviceid; -- в дальнейшем заменить получением userid по deviceid +BEGIN + -- Добавляем устройство, если его еще не существует + -- Если ID устройства еще нет в БД + IF NOT EXISTS(SELECT recid + FROM devices + WHERE recid = i_deviceid) + THEN + + -- Добавляем нового пользователя + INSERT INTO users (recid, recname, reccreated) SELECT + i_userid, + ''New user recname'', + now() + WHERE NOT EXISTS(SELECT recid FROM users WHERE recid = i_userid); + + -- Добавляем новое устройство + INSERT INTO devices (recid, userid, recname, reccreated) SELECT + i_deviceid, + i_userid, + ''New device recname'', + now(); + ELSE + SELECT (SELECT userid + FROM devices + WHERE recid = i_deviceid + LIMIT 1) + INTO i_userid; + END IF; + + -- Возвращаем trackid, конвертируя его в character varying, и methodid + RETURN QUERY SELECT + CAST((nexttrack.track) AS CHARACTER VARYING), + nexttrack.methodid, + CAST((nexttrack.useridrecommended) AS CHARACTER VARYING), + nexttrack.txtrecommendedinfo, + CAST((clock_timestamp() - t ) AS CHARACTER VARYING) -- возвращаем время выполнения процедуры + FROM getnexttrackid_v13(i_deviceid) AS nexttrack; +END; +' +LANGUAGE plpgsql; + +ALTER FUNCTION getnexttrack_v2(uuid) OWNER TO postgres; +ALTER FUNCTION calculateratios() OWNER TO postgres; +ALTER FUNCTION updateratios(uuid) OWNER TO postgres; +ALTER FUNCTION getlastdevices() OWNER TO postgres; +ALTER FUNCTION getlasttracks(uuid, integer) OWNER TO postgres; +ALTER FUNCTION getnexttrack(uuid) OWNER TO postgres; +ALTER FUNCTION getnexttrackid(uuid) OWNER TO postgres; +ALTER FUNCTION gettrackshistorybydevice(uuid, integer) OWNER TO postgres; +ALTER FUNCTION getuserdevices(uuid) OWNER TO postgres; +ALTER FUNCTION getusersrating(integer) OWNER TO postgres; +ALTER FUNCTION registerdevice(uuid, character varying) OWNER TO postgres; +ALTER FUNCTION registertrack(uuid, character varying, character varying, uuid) OWNER TO postgres; \ No newline at end of file diff --git a/v3/src/test/java/ownradio/ApplicationTests.java b/v3/src/test/java/ownradio/ApplicationTests.java new file mode 100644 index 0000000..62ef2c1 --- /dev/null +++ b/v3/src/test/java/ownradio/ApplicationTests.java @@ -0,0 +1,18 @@ +package ownradio; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +@ActiveProfiles("dev") +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/v3/src/test/java/ownradio/domain/UserTest.java b/v3/src/test/java/ownradio/domain/UserTest.java new file mode 100644 index 0000000..c8d24be --- /dev/null +++ b/v3/src/test/java/ownradio/domain/UserTest.java @@ -0,0 +1,44 @@ +package ownradio.domain; + +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static ownradio.util.ReflectUtil.getDisplayNameFields; + +public class UserTest { + + private Map expected; + + @Before + public void setUp() throws Exception { + expected = new HashMap<>(); + } + + @Test + public void showDisplayNameRu() throws Exception { + Locale.setDefault(new Locale("ru")); + + expected.put("id", "Название идентификатора на ru"); + + Map actual = getDisplayNameFields(new User()); + + assertThat(actual, equalTo(expected)); + } + + @Test + public void showDisplayNameEn() throws Exception { + Locale.setDefault(new Locale("en")); + + expected.put("id", "Название идентификатора на en"); + + Map actual = getDisplayNameFields(new User()); + + assertThat(actual, equalTo(expected)); + } +} \ No newline at end of file diff --git a/v3/src/test/java/ownradio/repository/HistoryRepositoryTest.java b/v3/src/test/java/ownradio/repository/HistoryRepositoryTest.java new file mode 100644 index 0000000..d46e2aa --- /dev/null +++ b/v3/src/test/java/ownradio/repository/HistoryRepositoryTest.java @@ -0,0 +1,68 @@ +package ownradio.repository; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.context.junit4.SpringRunner; +import ownradio.domain.Device; +import ownradio.domain.History; +import ownradio.domain.Track; +import ownradio.domain.User; + +import java.util.Calendar; +import java.util.Date; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +@RunWith(SpringRunner.class) +@DataJpaTest +public class HistoryRepositoryTest { + @Autowired + private HistoryRepository historyRepository; + + @Autowired + private TestEntityManager entityManager; + + private History history; + + @Before + public void setUp() throws Exception { + User user = entityManager.persist(new User()); + Device device = entityManager.persist(new Device(user, "1")); + Track track = entityManager.persist(new Track("1", device, "1", 0, "", 0, null, null, null, 1)); + + history = new History(track, Calendar.getInstance(), 0, "post", 1, device, 1, ""); + entityManager.persist(history); + } + + @Test + public void createdAt() throws Exception { + assertThat(history.getReccreated(), not(nullValue())); + assertThat(history.getReccreated().getTime().toString(), is(Calendar.getInstance().getTime().toString())); + } + + @Test + public void updatedAt() throws Exception { + assertThat(history.getReccreated(), not(nullValue())); + assertThat(history.getReccreated().getTime().toString(), is(Calendar.getInstance().getTime().toString())); + assertThat(history.getRecupdated(), is(nullValue())); + + History storeHistory = historyRepository.findOne(history.getRecid()); + storeHistory.setIsListen(1); + historyRepository.saveAndFlush(storeHistory); + + assertThat(storeHistory.getReccreated(), not(nullValue())); + assertThat(storeHistory.getReccreated().getTime().toString(), is(history.getReccreated().getTime().toString())); + + assertThat(storeHistory.getRecupdated(), not(nullValue())); + assertThat(storeHistory.getRecupdated().getTime().toString(), is(Calendar.getInstance().getTime().toString())); + } + + +} \ No newline at end of file diff --git a/v3/src/test/java/ownradio/repository/RatingRepositoryTest.java b/v3/src/test/java/ownradio/repository/RatingRepositoryTest.java new file mode 100644 index 0000000..3930678 --- /dev/null +++ b/v3/src/test/java/ownradio/repository/RatingRepositoryTest.java @@ -0,0 +1,45 @@ +package ownradio.repository; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import ownradio.domain.Rating; +import ownradio.domain.Track; +import ownradio.domain.User; + +import java.util.UUID; + +import static org.junit.Assert.*; + +/** + * Created by a.polunina on 10.11.2016. + */ +@ActiveProfiles("prod") +@RunWith(SpringRunner.class) +@SpringBootTest +//@DataJpaTest +public class RatingRepositoryTest { + @Autowired + private RatingRepository ratingRepository; + @Test + public void findByUser() throws Exception { + User user = new User(); + user.setRecid(UUID.fromString("bfa8137b-c917-4496-8fe7-39202322d257")); + Rating rating = ratingRepository.findByUser(user); + System.out.println(rating); + } + + @Test + public void findByUserAndTrack() throws Exception { + User user = new User(); + Track track = new Track(); + user.setRecid(UUID.fromString("bfa8137b-c917-4496-8fe7-39202322d257")); + track.setRecid(UUID.fromString("bfa8137b-c917-4496-8fe7-39202322d257")); + Rating rating = ratingRepository.findByUserAndTrack(user, track); + System.out.println(rating); + } +} \ No newline at end of file diff --git a/v3/src/test/java/ownradio/repository/TrackRepositoryTest.java b/v3/src/test/java/ownradio/repository/TrackRepositoryTest.java new file mode 100644 index 0000000..b4fdc63 --- /dev/null +++ b/v3/src/test/java/ownradio/repository/TrackRepositoryTest.java @@ -0,0 +1,57 @@ +package ownradio.repository; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import ownradio.domain.Device; +import ownradio.domain.Track; +import ownradio.domain.User; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import static org.junit.Assert.assertTrue; + +@ActiveProfiles("prod") +@RunWith(SpringRunner.class) +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +public class TrackRepositoryTest { + @Autowired + private TrackRepository trackRepository; + + @Autowired + private TestEntityManager entityManager; + + private User user; + private Device device; + + @Before + public void setUp() throws Exception { + user = entityManager.persist(new User()); + device = entityManager.persist(new Device(user, "123")); + entityManager.persist(new Track("1", device, "1", 0, "", 0, null, null, null, 1)); + entityManager.persist(new Track("2", device, "1", 0, "", 0, null, null, null, 1)); + entityManager.persist(new Track("4", device, "1", 0, "", 0, null, null, null, 1)); + } + + @Test + public void getNextTrackId() throws Exception { + Set trackSet = new HashSet<>(); + + for (int i = 0; i < 3; i++) { + UUID track = trackRepository.getNextTrackId(device.getRecid()); + trackSet.add(track); + } + + assertTrue(trackSet.size() > 1); + } + +} \ No newline at end of file diff --git a/v3/src/test/java/ownradio/service/TrackServiceTest.java b/v3/src/test/java/ownradio/service/TrackServiceTest.java new file mode 100644 index 0000000..abe1f81 --- /dev/null +++ b/v3/src/test/java/ownradio/service/TrackServiceTest.java @@ -0,0 +1,76 @@ +package ownradio.service; + +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringRunner; +import ownradio.domain.Device; +import ownradio.domain.Track; +import ownradio.domain.User; +import ownradio.repository.TrackRepository; +import ownradio.service.impl.TrackServiceImpl; + +import java.io.File; +import java.util.UUID; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static ownradio.util.ResourceUtil.UPLOAD_DIR; + +@RunWith(SpringRunner.class) +public class TrackServiceTest { + @Mock + private TrackRepository trackRepository; + + protected TrackService trackService; + + private UUID userId = UUID.randomUUID(); + private UUID trackId = UUID.randomUUID(); + private UUID deviceId = UUID.randomUUID(); + private Track expected; + + @Before + public void setUp() throws Exception { + trackService = new TrackServiceImpl(trackRepository); + expected = new Track(); + expected.setRecid(trackId); + + User user = new User(); + user.setRecid(userId); + + Device device = new Device(user, "123"); + device.setRecid(deviceId); + expected.setDevice(device); + } + + @After + public void tearDown() throws Exception { + FileUtils.deleteDirectory(new File(UPLOAD_DIR)); + } + + @Test + public void getNextTrackId() throws Exception { + given(this.trackRepository.getNextTrackId(trackId)).willReturn(trackId); + + UUID actual = trackService.getNextTrackId(trackId); + + assertThat(actual, equalTo(expected.getRecid())); + } + + @Test + public void save() throws Exception { + MockMultipartFile correctFile = new MockMultipartFile("file", "test.mp3", "text/plain", "Text".getBytes()); + + given(this.trackRepository.registerTrack(expected.getRecid(), expected.getLocaldevicepathupload(), expected.getPath(), expected.getDevice().getRecid())).willReturn(true); + given(this.trackRepository.findOne(expected.getRecid())).willReturn(expected); + trackService.save(expected, correctFile); + + assertThat(new File(expected.getPath()).exists(), is(true)); + } +} \ No newline at end of file diff --git a/v3/src/test/java/ownradio/web/rest/v2/HistoryControllerTest.java b/v3/src/test/java/ownradio/web/rest/v2/HistoryControllerTest.java new file mode 100644 index 0000000..97c2273 --- /dev/null +++ b/v3/src/test/java/ownradio/web/rest/v2/HistoryControllerTest.java @@ -0,0 +1,119 @@ +package ownradio.web.rest.v2; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import ownradio.domain.Device; +import ownradio.domain.History; +import ownradio.domain.Track; +import ownradio.domain.User; +import ownradio.service.DeviceService; +import ownradio.service.HistoryService; +import ownradio.service.TrackService; +import ownradio.service.UserService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Mockito.doThrow; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(HistoryController.class) +public class HistoryControllerTest { + public static final UUID TRACK_UUID = UUID.randomUUID(); + public static final UUID USER_UUID = UUID.randomUUID(); + public static final UUID DEVICE_UUID = UUID.randomUUID(); + + @MockBean + private HistoryService historyService; + + @MockBean + private UserService userService; + + @MockBean + private TrackService trackService; + + @MockBean + private DeviceService deviceService; + + @Autowired + private MockMvc mockMvc; + + private ObjectMapper mapper = new ObjectMapper(); + + private User user; + private Track track; + private Device device; + + @Before + public void setUp() throws Exception { + user = new User(); + track = new Track(); + device = new Device(); + } + + @Test + public void saveStatusIsOk() throws Exception { + given(this.userService.getById(USER_UUID)).willReturn(user); + given(this.trackService.getById(TRACK_UUID)).willReturn(track); + given(this.deviceService.getById(DEVICE_UUID)).willReturn(device); + + JSONObject obj = new JSONObject(); + obj.put("lastListen", "2016-11-28T12:34:56"); + obj.put("isListen", "1"); + obj.put("method", "method"); + + mockMvc.perform(post("/v2/histories/{deviceId}/{trackId}", DEVICE_UUID, TRACK_UUID) + .contentType(MediaType.APPLICATION_JSON) + .content(obj.toString()) +// .param("lastListen", "2016-11-28 12:34:56") +// .param("isListen", "1") +// .param("method", "method") + ) + .andDo(print()) + .andExpect( + status().isOk() + ); + } + + @Test + public void saveStatusIsInternalServerError() throws Exception { + given(this.userService.getById(USER_UUID)).willReturn(user); + given(this.trackService.getById(TRACK_UUID)).willReturn(track); + given(this.deviceService.getById(DEVICE_UUID)).willReturn(device); + + doThrow(RuntimeException.class).when(this.historyService).save(any(History.class), anyBoolean()); + + JSONObject obj = new JSONObject(); + obj.put("lastListen", "2016-11-28T12:34:56"); + obj.put("isListen", "1"); + obj.put("method", "method"); + + mockMvc.perform(post("/v2/histories/{deviceId}/{trackId}", DEVICE_UUID, TRACK_UUID) + .contentType(MediaType.APPLICATION_JSON) + .content(obj.toString()) +// .param("lastListen", "2016-11-28 12:34:56") +// .param("isListen", "1") +// .param("method", "method") + ) + .andDo(print()) + .andExpect( + status().isInternalServerError() + ); + } + + +} \ No newline at end of file diff --git a/v3/src/test/java/ownradio/web/rest/v2/TrackControllerTest.java b/v3/src/test/java/ownradio/web/rest/v2/TrackControllerTest.java new file mode 100644 index 0000000..b36bcb6 --- /dev/null +++ b/v3/src/test/java/ownradio/web/rest/v2/TrackControllerTest.java @@ -0,0 +1,185 @@ +package ownradio.web.rest.v2; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.json.JSONObject; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import ownradio.domain.Device; +import ownradio.domain.Track; +import ownradio.domain.User; +import ownradio.service.DeviceService; +import ownradio.service.TrackService; +import ownradio.util.ResourceUtil; + +import java.io.File; +import java.util.UUID; + +import static org.hamcrest.core.Is.is; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ownradio.util.ResourceUtil.UPLOAD_DIR; + +@RunWith(SpringRunner.class) +@WebMvcTest(TrackController.class) +public class TrackControllerTest { + public static final UUID TRACK_UUID = UUID.randomUUID(); + public static final UUID USER_UUID = UUID.randomUUID(); + public static final UUID DEVICE_UUID = UUID.randomUUID(); + public static final String FILE = TRACK_UUID + ".mp3"; + public static final String PATH = UPLOAD_DIR + USER_UUID + "/" + FILE; + + @MockBean + private TrackService trackService; + + @MockBean + private DeviceService deviceService; + + @Autowired + protected MockMvc mockMvc; + + private MockMultipartFile correctFile; + private MockMultipartFile emptyFile; + + private ObjectMapper mapper = new ObjectMapper(); + + private User user = new User(); + private Device device = new Device(); + private Track track; + + @Before + public void setUp() throws Exception { + user.setRecid(USER_UUID); + device.setRecid(DEVICE_UUID); + device.setUser(user); + + track = new Track(PATH, device, "---", 0, "", 0, null, null, null, 1); + + String requestParam = "musicFile"; + String originalFilename = "test.mp3"; + String contentType = "audio/mpeg"; + + correctFile = new MockMultipartFile(requestParam, originalFilename, contentType, "Text".getBytes()); + emptyFile = new MockMultipartFile(requestParam, originalFilename, contentType, "".getBytes()); + + ResourceUtil.save(USER_UUID.toString(), FILE, correctFile); + } + + @After + public void tearDown() throws Exception { + FileUtils.deleteDirectory(new File(UPLOAD_DIR)); + } + + @Test + public void saveStatusIsOk() throws Exception { + given(this.deviceService.getById(DEVICE_UUID)).willReturn(device); + + JSONObject obj = new JSONObject(); + obj.put("fileGuid", TRACK_UUID.toString()); + obj.put("fileName", correctFile.getOriginalFilename()); + obj.put("filePath", PATH); + obj.put("deviceId", DEVICE_UUID.toString()); + + mockMvc.perform(fileUpload("/v2/tracks") + .file(correctFile) + .accept(MediaType.APPLICATION_JSON_UTF8_VALUE) + .content(obj.toString()) + .contentType(MediaType.MULTIPART_FORM_DATA) +// .param("fileGuid", TRACK_UUID.toString()) +// .param("fileName", correctFile.getOriginalFilename()) +// .param("filePath", PATH) +// .param("deviceId", DEVICE_UUID.toString()) + ) + .andDo(print()) + .andExpect( + status().isCreated() + ); + } + + @Test + public void saveStatusIsBadRequest() throws Exception { + mockMvc.perform(fileUpload("/v2/tracks") + .file(emptyFile) + .accept(MediaType.APPLICATION_JSON_UTF8_VALUE) + .param("fileGuid", TRACK_UUID.toString()) + .param("fileName", correctFile.getOriginalFilename()) + .param("filePath", PATH) + .param("deviceId", DEVICE_UUID.toString()) + ) + .andDo(print()) + .andExpect( + status().isBadRequest() + ); + } + + @Test + public void getTrackStatusIsOk() throws Exception { + given(this.trackService.getById(TRACK_UUID)).willReturn(track); + + mockMvc.perform(get("/v2/tracks/{trackId}", TRACK_UUID).accept(MediaType.TEXT_PLAIN)) + .andDo(print()) + .andExpect( + status().isOk() + ) + .andExpect( + header().string("Content-Type", is("audio/mpeg")) + ) + .andExpect( + content().string("Text") + ); + } + + @Test + public void getTrackStatusIsNotFound() throws Exception { + + given(this.trackService.getById(TRACK_UUID)).willReturn(null); + + mockMvc.perform(get("/v2/tracks/{trackId}", TRACK_UUID).accept(MediaType.TEXT_PLAIN)) + .andDo(print()) + .andExpect( + status().isNotFound() + ); + } + + @Test + public void getNextTrackIdIsOk() throws Exception { + given(this.trackService.getNextTrackId(DEVICE_UUID)).willReturn(TRACK_UUID); + + mockMvc.perform(get("/v2/tracks/{deviceId}/next", DEVICE_UUID)) + .andDo(print()) + .andExpect( + status().isOk() + ) + .andExpect( + content().string(mapper.writeValueAsString(TRACK_UUID)) + ); + + } + + @Test + public void getNextTrackIdIsNotFound() throws Exception { + given(this.trackService.getNextTrackId(DEVICE_UUID)).willReturn(null); + + mockMvc.perform(get("/v2/tracks/{deviceId}/next", DEVICE_UUID)) + .andDo(print()) + .andExpect( + status().isNotFound() + ); + + } + +} \ No newline at end of file