added inittal assessment feature
This commit is contained in:
parent
915185c317
commit
2c907a34db
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
9
.idea/Yimaru Backend.iml
Normal file
9
.idea/Yimaru Backend.iml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Yimaru Backend.iml" filepath="$PROJECT_DIR$/.idea/Yimaru Backend.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"Yimaru-Backend/internal/logger/mongoLogger"
|
||||
"Yimaru-Backend/internal/repository"
|
||||
"Yimaru-Backend/internal/services/arifpay"
|
||||
"Yimaru-Backend/internal/services/assessment"
|
||||
"Yimaru-Backend/internal/services/authentication"
|
||||
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
|
||||
"Yimaru-Backend/internal/services/messenger"
|
||||
|
|
@ -323,6 +324,13 @@ func main() {
|
|||
// transferStore := repository.NewTransferStore(store)
|
||||
// walletStore := wallet.WalletStore(store)
|
||||
|
||||
assessmentSvc := assessment.NewService(
|
||||
repository.NewUserStore(store),
|
||||
repository.NewInitialAssessmentStore(store),
|
||||
notificationSvc,
|
||||
cfg,
|
||||
)
|
||||
|
||||
arifpaySvc := arifpay.NewArifpayService(cfg, *transactionSvc, &http.Client{
|
||||
Timeout: 30 * time.Second})
|
||||
|
||||
|
|
@ -333,6 +341,7 @@ func main() {
|
|||
|
||||
// Initialize and start HTTP server
|
||||
app := httpserver.NewApp(
|
||||
assessmentSvc,
|
||||
arifpaySvc,
|
||||
issueReportingSvc,
|
||||
cfg.Port,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,40 @@
|
|||
CREATE TABLE assessment_questions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
question_type VARCHAR(50) NOT NULL, -- MULTIPLE_CHOICE, TRUE_FALSE, SHORT_ANSWER
|
||||
difficulty_level VARCHAR(50) NOT NULL, -- BEGINNER, INTERMEDIATE, ADVANCED
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE TABLE assessment_question_options (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
question_id BIGINT NOT NULL REFERENCES assessment_questions(id) ON DELETE CASCADE,
|
||||
option_text TEXT NOT NULL,
|
||||
is_correct BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE TABLE assessment_attempts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
total_questions INT NOT NULL,
|
||||
correct_answers INT NOT NULL,
|
||||
score_percentage NUMERIC(5,2) NOT NULL,
|
||||
knowledge_level VARCHAR(50) NOT NULL, -- BEGINNER, INTERMEDIATE, ADVANCED
|
||||
completed_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE assessment_answers (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
attempt_id BIGINT NOT NULL REFERENCES assessment_attempts(id) ON DELETE CASCADE,
|
||||
question_id BIGINT NOT NULL REFERENCES assessment_questions(id),
|
||||
selected_option_id BIGINT REFERENCES assessment_question_options(id),
|
||||
short_answer TEXT,
|
||||
is_correct BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
first_name VARCHAR(255) NOT NULL,
|
||||
|
|
@ -12,6 +49,16 @@ CREATE TABLE IF NOT EXISTS users (
|
|||
education_level VARCHAR(100),
|
||||
country VARCHAR(100),
|
||||
region VARCHAR(100),
|
||||
|
||||
knowledge_level VARCHAR(50), -- BEGINNER, INTERMEDIATE, ADVANCED
|
||||
nick_name VARCHAR(100),
|
||||
occupation VARCHAR(150),
|
||||
learning_goal TEXT,
|
||||
language_goal TEXT,
|
||||
language_challange TEXT,
|
||||
favoutite_topic TEXT,
|
||||
|
||||
initial_assessment_completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
phone_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
status VARCHAR(50) NOT NULL, -- PENDING, ACTIVE, SUSPENDED, DEACTIVATED
|
||||
|
|
@ -26,7 +73,6 @@ CREATE TABLE IF NOT EXISTS users (
|
|||
CHECK (email IS NOT NULL OR phone_number IS NOT NULL)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE refresh_tokens (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
|
|
|||
93
db/query/initial_assessment.sql
Normal file
93
db/query/initial_assessment.sql
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
-- name: CreateAssessmentAttempt :one
|
||||
INSERT INTO assessment_attempts (
|
||||
user_id,
|
||||
total_questions,
|
||||
correct_answers,
|
||||
score_percentage,
|
||||
knowledge_level
|
||||
)
|
||||
VALUES (
|
||||
$1, -- user_id
|
||||
$2, -- total_questions
|
||||
$3, -- correct_answers
|
||||
$4, -- score_percentage
|
||||
$5 -- knowledge_level
|
||||
)
|
||||
RETURNING
|
||||
id,
|
||||
user_id,
|
||||
total_questions,
|
||||
correct_answers,
|
||||
score_percentage,
|
||||
knowledge_level,
|
||||
completed_at;
|
||||
|
||||
-- name: CreateAssessmentAnswer :exec
|
||||
INSERT INTO assessment_answers (
|
||||
attempt_id,
|
||||
question_id,
|
||||
selected_option_id,
|
||||
short_answer,
|
||||
is_correct
|
||||
)
|
||||
VALUES (
|
||||
$1, -- attempt_id
|
||||
$2, -- question_id
|
||||
$3, -- selected_option_id
|
||||
$4, -- short_answer
|
||||
$5 -- is_correct
|
||||
);
|
||||
|
||||
-- name: GetAssessmentOptionByID :one
|
||||
SELECT
|
||||
id,
|
||||
question_id,
|
||||
option_text,
|
||||
is_correct
|
||||
FROM assessment_question_options
|
||||
WHERE id = $1
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetCorrectOptionForQuestion :one
|
||||
SELECT
|
||||
id
|
||||
FROM assessment_question_options
|
||||
WHERE question_id = $1
|
||||
AND is_correct = TRUE
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetLatestAssessmentAttempt :one
|
||||
SELECT *
|
||||
FROM assessment_attempts
|
||||
WHERE user_id = $1
|
||||
ORDER BY completed_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- name: CreateAssessmentQuestion :one
|
||||
INSERT INTO assessment_questions (
|
||||
title,
|
||||
description,
|
||||
question_type,
|
||||
difficulty_level
|
||||
)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *;
|
||||
|
||||
-- name: CreateAssessmentQuestionOption :exec
|
||||
INSERT INTO assessment_question_options (
|
||||
question_id,
|
||||
option_text,
|
||||
is_correct
|
||||
)
|
||||
VALUES ($1, $2, $3);
|
||||
|
||||
-- name: GetActiveAssessmentQuestions :many
|
||||
SELECT *
|
||||
FROM assessment_questions
|
||||
WHERE is_active = TRUE
|
||||
ORDER BY difficulty_level, id;
|
||||
|
||||
-- name: GetQuestionOptions :many
|
||||
SELECT *
|
||||
FROM assessment_question_options
|
||||
WHERE question_id = $1;
|
||||
|
|
@ -25,30 +25,50 @@ INSERT INTO users (
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
profile_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
updated_at
|
||||
)
|
||||
VALUES (
|
||||
$1, -- first_name
|
||||
$2, -- last_name
|
||||
$3, -- user_name
|
||||
$4, -- email
|
||||
$5, -- phone_number
|
||||
$6, -- role
|
||||
$7, -- password (BYTEA)
|
||||
$8, -- age
|
||||
$9, -- education_level
|
||||
$10, -- country
|
||||
$11, -- region
|
||||
$12, -- email_verified
|
||||
$13, -- phone_verified
|
||||
$14, -- status (PENDING | ACTIVE)
|
||||
$15, -- profile_completed
|
||||
$16, -- preferred_language
|
||||
$1, -- first_name
|
||||
$2, -- last_name
|
||||
$3, -- user_name
|
||||
$4, -- email
|
||||
$5, -- phone_number
|
||||
$6, -- role
|
||||
$7, -- password
|
||||
$8, -- age
|
||||
$9, -- education_level
|
||||
$10, -- country
|
||||
$11, -- region
|
||||
|
||||
$12, -- nick_name
|
||||
$13, -- occupation
|
||||
$14, -- learning_goal
|
||||
$15, -- language_goal
|
||||
$16, -- language_challange
|
||||
$17, -- favoutite_topic
|
||||
|
||||
$18, -- initial_assessment_completed
|
||||
$19, -- email_verified
|
||||
$20, -- phone_verified
|
||||
$21, -- status
|
||||
$22, -- profile_completed
|
||||
$23, -- profile_picture_url
|
||||
$24, -- preferred_language
|
||||
CURRENT_TIMESTAMP
|
||||
)
|
||||
RETURNING
|
||||
|
|
@ -63,10 +83,20 @@ RETURNING
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
profile_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
created_at,
|
||||
updated_at;
|
||||
|
|
@ -90,6 +120,17 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -137,6 +178,17 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -158,11 +210,30 @@ WHERE (
|
|||
-- name: UpdateUser :exec
|
||||
UPDATE users
|
||||
SET
|
||||
first_name = $1,
|
||||
last_name = $2,
|
||||
status = $3,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $4;
|
||||
first_name = $1,
|
||||
last_name = $2,
|
||||
user_name = $3,
|
||||
age = $4,
|
||||
education_level = $5,
|
||||
country = $6,
|
||||
region = $7,
|
||||
|
||||
nick_name = $8,
|
||||
occupation = $9,
|
||||
learning_goal = $10,
|
||||
language_goal = $11,
|
||||
language_challange = $12,
|
||||
favoutite_topic = $13,
|
||||
|
||||
initial_assessment_completed = $14,
|
||||
email_verified = $15,
|
||||
phone_verified = $16,
|
||||
status = $17,
|
||||
profile_completed = $18,
|
||||
profile_picture_url = $19,
|
||||
preferred_language = $20,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $21;
|
||||
|
||||
-- name: DeleteUser :exec
|
||||
DELETE FROM users
|
||||
|
|
@ -171,14 +242,10 @@ WHERE id = $1;
|
|||
-- name: CheckPhoneEmailExist :one
|
||||
SELECT
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM users u1
|
||||
WHERE u1.phone_number = $1
|
||||
SELECT 1 FROM users u1 WHERE u1.phone_number = $1
|
||||
) AS phone_exists,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM users u2
|
||||
WHERE u2.email = $2
|
||||
SELECT 1 FROM users u2 WHERE u2.email = $2
|
||||
) AS email_exists;
|
||||
|
||||
-- name: GetUserByUserName :one
|
||||
|
|
@ -195,6 +262,14 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -222,6 +297,14 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -233,7 +316,7 @@ SELECT
|
|||
updated_at
|
||||
FROM users
|
||||
WHERE (email = $1 AND $1 IS NOT NULL)
|
||||
OR (phone_number = $2 AND $2 IS NOT NULL)
|
||||
OR (phone_number = $2 AND $2 IS NOT NULL)
|
||||
LIMIT 1;
|
||||
|
||||
-- name: UpdatePassword :exec
|
||||
|
|
@ -241,7 +324,7 @@ UPDATE users
|
|||
SET
|
||||
password = $1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = $2 OR phone_number = $3;
|
||||
WHERE user_name = $2;
|
||||
|
||||
-- name: UpdateUserStatus :exec
|
||||
UPDATE users
|
||||
|
|
@ -249,3 +332,10 @@ SET
|
|||
status = $1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2;
|
||||
|
||||
-- name: UpdateUserKnowledgeLevel :exec
|
||||
UPDATE users
|
||||
SET
|
||||
knowledge_level = $1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2;
|
||||
|
|
|
|||
690
docs/docs.go
690
docs/docs.go
|
|
@ -229,9 +229,109 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/assessment/questions": {
|
||||
"get": {
|
||||
"description": "Returns all active questions used for initial knowledge assessment",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"assessment"
|
||||
],
|
||||
"summary": "Get active initial assessment questions",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Creates a new question for the initial knowledge assessment",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"assessment"
|
||||
],
|
||||
"summary": "Create assessment question",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Assessment question payload",
|
||||
"name": "question",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/logout": {
|
||||
"post": {
|
||||
"description": "Logout customer",
|
||||
"description": "Logout user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -241,10 +341,10 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Logout customer",
|
||||
"summary": "Logout user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Logout customer",
|
||||
"description": "Logout user",
|
||||
"name": "logout",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
|
|
@ -309,7 +409,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginUserRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -393,6 +493,52 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/sendSMS": {
|
||||
"post": {
|
||||
"description": "Sends an SMS message to a single phone number using AfroMessage",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Send single SMS via AfroMessage",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Send SMS request",
|
||||
"name": "sendSMS",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.SendSingleAfroSMSReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/super-login": {
|
||||
"post": {
|
||||
"description": "Login super-admin",
|
||||
|
|
@ -823,6 +969,52 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/user/verify-otp": {
|
||||
"post": {
|
||||
"description": "Verify OTP for registration or other actions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Verify OTP",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Verify OTP",
|
||||
"name": "verifyOtp",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.VerifyOtpReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/user/{user_name}/is-unique": {
|
||||
"get": {
|
||||
"description": "Returns whether the specified user_name is available (unique)",
|
||||
|
|
@ -869,7 +1061,7 @@ const docTemplate = `{
|
|||
},
|
||||
"/api/v1/{tenant_slug}/admin-login": {
|
||||
"post": {
|
||||
"description": "Login customer",
|
||||
"description": "Login user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -879,7 +1071,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Login customer",
|
||||
"summary": "Login user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Login admin",
|
||||
|
|
@ -919,9 +1111,114 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/customer-login": {
|
||||
"/api/v1/{tenant_slug}/assessment/submit": {
|
||||
"post": {
|
||||
"description": "Login customer",
|
||||
"description": "Evaluates user responses, calculates knowledge level, updates user profile, and sends notification",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"assessment"
|
||||
],
|
||||
"summary": "Submit initial knowledge assessment",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Assessment responses",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.SubmitAssessmentReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/otp/resend": {
|
||||
"post": {
|
||||
"description": "Resend OTP if the previous one is expired",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"otp"
|
||||
],
|
||||
"summary": "Resend OTP",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Resend OTP",
|
||||
"name": "resendOtp",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ResendOtpReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/user-login": {
|
||||
"post": {
|
||||
"description": "Login user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -931,15 +1228,15 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Login customer",
|
||||
"summary": "Login user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Login customer",
|
||||
"description": "Login user",
|
||||
"name": "login",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.loginCustomerReq"
|
||||
"$ref": "#/definitions/handlers.loginUserReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -947,7 +1244,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginUserRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -1057,14 +1354,9 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/user/customer-profile": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Get user profile",
|
||||
"/api/v1/{tenant_slug}/user/knowledge-level": {
|
||||
"put": {
|
||||
"description": "Updates the knowledge level of the specified user after initial assessment",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -1074,24 +1366,48 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get user profile",
|
||||
"summary": "Update user's knowledge level",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Knowledge level",
|
||||
"name": "knowledge_level",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.UpdateKnowledgeLevelReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.CustomerProfileRes"
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1281,9 +1597,14 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/user/verify-otp": {
|
||||
"post": {
|
||||
"description": "Verify OTP for registration or other actions",
|
||||
"/api/v1/{tenant_slug}/user/user-profile": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Get user profile",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -1293,23 +1614,12 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Verify OTP",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Verify OTP",
|
||||
"name": "verifyOtp",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.VerifyOtpReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "Get user profile",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
"$ref": "#/definitions/domain.UserProfileResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -1379,6 +1689,48 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"domain.AssessmentOption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"isCorrect": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"optionText": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.AssessmentQuestion": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"difficultyLevel": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.AssessmentOption"
|
||||
}
|
||||
},
|
||||
"questionType": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1437,17 +1789,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"domain.OtpFor": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"reset",
|
||||
"register"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"OtpReset",
|
||||
"OtpRegister"
|
||||
]
|
||||
},
|
||||
"domain.OtpMedium": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -1485,34 +1826,46 @@ const docTemplate = `{
|
|||
"country": {
|
||||
"type": "string"
|
||||
},
|
||||
"educationLevel": {
|
||||
"education_level": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"firstName": {
|
||||
"favoutite_topic": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organizationID": {
|
||||
"$ref": "#/definitions/domain.ValidInt64"
|
||||
},
|
||||
"otp": {
|
||||
"language_challange": {
|
||||
"type": "string"
|
||||
},
|
||||
"otpMedium": {
|
||||
"language_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"learning_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp_medium": {
|
||||
"$ref": "#/definitions/domain.OtpMedium"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
},
|
||||
"preferredLanguage": {
|
||||
"preferred_language": {
|
||||
"type": "string"
|
||||
},
|
||||
"region": {
|
||||
|
|
@ -1521,7 +1874,18 @@ const docTemplate = `{
|
|||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"userName": {
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.ResendOtpReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
@ -1559,6 +1923,52 @@ const docTemplate = `{
|
|||
"RoleSupport"
|
||||
]
|
||||
},
|
||||
"domain.SubmitAssessmentReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"answers"
|
||||
],
|
||||
"properties": {
|
||||
"answers": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.UserAnswer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UpdateKnowledgeLevelReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"knowledge_level": {
|
||||
"description": "BEGINNER, INTERMEDIATE, ADVANCED",
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UserAnswer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isCorrect": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"questionID": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"selectedOptionID": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"shortAnswer": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UserProfileResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1580,20 +1990,39 @@ const docTemplate = `{
|
|||
"email_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"favoutite_topic": {
|
||||
"type": "string"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"initial_assessment_completed": {
|
||||
"description": "Profile fields",
|
||||
"type": "boolean"
|
||||
},
|
||||
"language_challange": {
|
||||
"type": "string"
|
||||
},
|
||||
"language_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_login": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_id": {
|
||||
"type": "integer"
|
||||
"learning_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
|
|
@ -1642,48 +2071,17 @@ const docTemplate = `{
|
|||
"UserStatusDeactivated"
|
||||
]
|
||||
},
|
||||
"domain.ValidInt64": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"valid": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.VerifyOtpReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"otp",
|
||||
"otp_for",
|
||||
"otp_medium"
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Required if medium is email",
|
||||
"type": "string"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp_for": {
|
||||
"$ref": "#/definitions/domain.OtpFor"
|
||||
},
|
||||
"otp_medium": {
|
||||
"enum": [
|
||||
"email",
|
||||
"sms"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/domain.OtpMedium"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phone_number": {
|
||||
"description": "Required if medium is SMS",
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
@ -1803,10 +2201,6 @@ const docTemplate = `{
|
|||
"handlers.CreateAdminReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"company_id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
|
|
@ -1829,53 +2223,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"handlers.CustomerProfileRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"email_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last_login": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"referral_code": {
|
||||
"type": "string"
|
||||
},
|
||||
"role": {
|
||||
"$ref": "#/definitions/domain.Role"
|
||||
},
|
||||
"suspended": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"suspended_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.LoginAdminRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1920,13 +2267,10 @@ const docTemplate = `{
|
|||
"type": "object",
|
||||
"required": [
|
||||
"otp",
|
||||
"password"
|
||||
"password",
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string",
|
||||
"example": "123456"
|
||||
|
|
@ -1936,9 +2280,9 @@ const docTemplate = `{
|
|||
"minLength": 8,
|
||||
"example": "newpassword123"
|
||||
},
|
||||
"phone_number": {
|
||||
"user_name": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
"example": "johndoe"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1953,27 +2297,41 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"handlers.SendSingleAfroSMSReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message",
|
||||
"recipient"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Hello world"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string",
|
||||
"example": "+251912345678"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginAdminReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"password"
|
||||
"password",
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "password123"
|
||||
},
|
||||
"phone_number": {
|
||||
"user_name": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
"example": "adminuser"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginCustomerReq": {
|
||||
"handlers.loginUserReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"password",
|
||||
|
|
@ -1990,7 +2348,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginCustomerRes": {
|
||||
"handlers.loginUserRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
|
|
@ -2036,10 +2394,6 @@ const docTemplate = `{
|
|||
"handlers.updateAdminReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"company_id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string",
|
||||
"example": "John"
|
||||
|
|
|
|||
|
|
@ -221,9 +221,109 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/assessment/questions": {
|
||||
"get": {
|
||||
"description": "Returns all active questions used for initial knowledge assessment",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"assessment"
|
||||
],
|
||||
"summary": "Get active initial assessment questions",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Creates a new question for the initial knowledge assessment",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"assessment"
|
||||
],
|
||||
"summary": "Create assessment question",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Assessment question payload",
|
||||
"name": "question",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/logout": {
|
||||
"post": {
|
||||
"description": "Logout customer",
|
||||
"description": "Logout user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -233,10 +333,10 @@
|
|||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Logout customer",
|
||||
"summary": "Logout user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Logout customer",
|
||||
"description": "Logout user",
|
||||
"name": "logout",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
|
|
@ -301,7 +401,7 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginUserRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -385,6 +485,52 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/sendSMS": {
|
||||
"post": {
|
||||
"description": "Sends an SMS message to a single phone number using AfroMessage",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Send single SMS via AfroMessage",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Send SMS request",
|
||||
"name": "sendSMS",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.SendSingleAfroSMSReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/super-login": {
|
||||
"post": {
|
||||
"description": "Login super-admin",
|
||||
|
|
@ -815,6 +961,52 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/user/verify-otp": {
|
||||
"post": {
|
||||
"description": "Verify OTP for registration or other actions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Verify OTP",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Verify OTP",
|
||||
"name": "verifyOtp",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.VerifyOtpReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/user/{user_name}/is-unique": {
|
||||
"get": {
|
||||
"description": "Returns whether the specified user_name is available (unique)",
|
||||
|
|
@ -861,7 +1053,7 @@
|
|||
},
|
||||
"/api/v1/{tenant_slug}/admin-login": {
|
||||
"post": {
|
||||
"description": "Login customer",
|
||||
"description": "Login user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -871,7 +1063,7 @@
|
|||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Login customer",
|
||||
"summary": "Login user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Login admin",
|
||||
|
|
@ -911,9 +1103,114 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/customer-login": {
|
||||
"/api/v1/{tenant_slug}/assessment/submit": {
|
||||
"post": {
|
||||
"description": "Login customer",
|
||||
"description": "Evaluates user responses, calculates knowledge level, updates user profile, and sends notification",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"assessment"
|
||||
],
|
||||
"summary": "Submit initial knowledge assessment",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Assessment responses",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.SubmitAssessmentReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/otp/resend": {
|
||||
"post": {
|
||||
"description": "Resend OTP if the previous one is expired",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"otp"
|
||||
],
|
||||
"summary": "Resend OTP",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Resend OTP",
|
||||
"name": "resendOtp",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ResendOtpReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/user-login": {
|
||||
"post": {
|
||||
"description": "Login user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -923,15 +1220,15 @@
|
|||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Login customer",
|
||||
"summary": "Login user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Login customer",
|
||||
"description": "Login user",
|
||||
"name": "login",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.loginCustomerReq"
|
||||
"$ref": "#/definitions/handlers.loginUserReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -939,7 +1236,7 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginUserRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -1049,14 +1346,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/user/customer-profile": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Get user profile",
|
||||
"/api/v1/{tenant_slug}/user/knowledge-level": {
|
||||
"put": {
|
||||
"description": "Updates the knowledge level of the specified user after initial assessment",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -1066,24 +1358,48 @@
|
|||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get user profile",
|
||||
"summary": "Update user's knowledge level",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Knowledge level",
|
||||
"name": "knowledge_level",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.UpdateKnowledgeLevelReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.CustomerProfileRes"
|
||||
"$ref": "#/definitions/domain.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1273,9 +1589,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{tenant_slug}/user/verify-otp": {
|
||||
"post": {
|
||||
"description": "Verify OTP for registration or other actions",
|
||||
"/api/v1/{tenant_slug}/user/user-profile": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Get user profile",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -1285,23 +1606,12 @@
|
|||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Verify OTP",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Verify OTP",
|
||||
"name": "verifyOtp",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.VerifyOtpReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "Get user profile",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
"$ref": "#/definitions/domain.UserProfileResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -1371,6 +1681,48 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"domain.AssessmentOption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"isCorrect": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"optionText": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.AssessmentQuestion": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"difficultyLevel": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.AssessmentOption"
|
||||
}
|
||||
},
|
||||
"questionType": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1429,17 +1781,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"domain.OtpFor": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"reset",
|
||||
"register"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"OtpReset",
|
||||
"OtpRegister"
|
||||
]
|
||||
},
|
||||
"domain.OtpMedium": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -1477,34 +1818,46 @@
|
|||
"country": {
|
||||
"type": "string"
|
||||
},
|
||||
"educationLevel": {
|
||||
"education_level": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"firstName": {
|
||||
"favoutite_topic": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organizationID": {
|
||||
"$ref": "#/definitions/domain.ValidInt64"
|
||||
},
|
||||
"otp": {
|
||||
"language_challange": {
|
||||
"type": "string"
|
||||
},
|
||||
"otpMedium": {
|
||||
"language_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"learning_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp_medium": {
|
||||
"$ref": "#/definitions/domain.OtpMedium"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
},
|
||||
"preferredLanguage": {
|
||||
"preferred_language": {
|
||||
"type": "string"
|
||||
},
|
||||
"region": {
|
||||
|
|
@ -1513,7 +1866,18 @@
|
|||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"userName": {
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.ResendOtpReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
@ -1551,6 +1915,52 @@
|
|||
"RoleSupport"
|
||||
]
|
||||
},
|
||||
"domain.SubmitAssessmentReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"answers"
|
||||
],
|
||||
"properties": {
|
||||
"answers": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.UserAnswer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UpdateKnowledgeLevelReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"knowledge_level": {
|
||||
"description": "BEGINNER, INTERMEDIATE, ADVANCED",
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UserAnswer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isCorrect": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"questionID": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"selectedOptionID": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"shortAnswer": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UserProfileResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1572,20 +1982,39 @@
|
|||
"email_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"favoutite_topic": {
|
||||
"type": "string"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"initial_assessment_completed": {
|
||||
"description": "Profile fields",
|
||||
"type": "boolean"
|
||||
},
|
||||
"language_challange": {
|
||||
"type": "string"
|
||||
},
|
||||
"language_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_login": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_id": {
|
||||
"type": "integer"
|
||||
"learning_goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
|
|
@ -1634,48 +2063,17 @@
|
|||
"UserStatusDeactivated"
|
||||
]
|
||||
},
|
||||
"domain.ValidInt64": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"valid": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.VerifyOtpReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"otp",
|
||||
"otp_for",
|
||||
"otp_medium"
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Required if medium is email",
|
||||
"type": "string"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp_for": {
|
||||
"$ref": "#/definitions/domain.OtpFor"
|
||||
},
|
||||
"otp_medium": {
|
||||
"enum": [
|
||||
"email",
|
||||
"sms"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/domain.OtpMedium"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phone_number": {
|
||||
"description": "Required if medium is SMS",
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
@ -1795,10 +2193,6 @@
|
|||
"handlers.CreateAdminReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"company_id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
|
|
@ -1821,53 +2215,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"handlers.CustomerProfileRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"email_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last_login": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"referral_code": {
|
||||
"type": "string"
|
||||
},
|
||||
"role": {
|
||||
"$ref": "#/definitions/domain.Role"
|
||||
},
|
||||
"suspended": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"suspended_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.LoginAdminRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1912,13 +2259,10 @@
|
|||
"type": "object",
|
||||
"required": [
|
||||
"otp",
|
||||
"password"
|
||||
"password",
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string",
|
||||
"example": "123456"
|
||||
|
|
@ -1928,9 +2272,9 @@
|
|||
"minLength": 8,
|
||||
"example": "newpassword123"
|
||||
},
|
||||
"phone_number": {
|
||||
"user_name": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
"example": "johndoe"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1945,27 +2289,41 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"handlers.SendSingleAfroSMSReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message",
|
||||
"recipient"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Hello world"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string",
|
||||
"example": "+251912345678"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginAdminReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"password"
|
||||
"password",
|
||||
"user_name"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "password123"
|
||||
},
|
||||
"phone_number": {
|
||||
"user_name": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
"example": "adminuser"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginCustomerReq": {
|
||||
"handlers.loginUserReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"password",
|
||||
|
|
@ -1982,7 +2340,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginCustomerRes": {
|
||||
"handlers.loginUserRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
|
|
@ -2028,10 +2386,6 @@
|
|||
"handlers.updateAdminReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"company_id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string",
|
||||
"example": "John"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,32 @@
|
|||
definitions:
|
||||
domain.AssessmentOption:
|
||||
properties:
|
||||
id:
|
||||
format: int64
|
||||
type: integer
|
||||
isCorrect:
|
||||
type: boolean
|
||||
optionText:
|
||||
type: string
|
||||
type: object
|
||||
domain.AssessmentQuestion:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
difficultyLevel:
|
||||
type: string
|
||||
id:
|
||||
format: int64
|
||||
type: integer
|
||||
options:
|
||||
items:
|
||||
$ref: '#/definitions/domain.AssessmentOption'
|
||||
type: array
|
||||
questionType:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
type: object
|
||||
domain.ErrorResponse:
|
||||
properties:
|
||||
error:
|
||||
|
|
@ -37,14 +65,6 @@ definitions:
|
|||
pagination:
|
||||
$ref: '#/definitions/domain.Pagination'
|
||||
type: object
|
||||
domain.OtpFor:
|
||||
enum:
|
||||
- reset
|
||||
- register
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- OtpReset
|
||||
- OtpRegister
|
||||
domain.OtpMedium:
|
||||
enum:
|
||||
- email
|
||||
|
|
@ -70,33 +90,48 @@ definitions:
|
|||
type: integer
|
||||
country:
|
||||
type: string
|
||||
educationLevel:
|
||||
education_level:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
firstName:
|
||||
favoutite_topic:
|
||||
type: string
|
||||
lastName:
|
||||
first_name:
|
||||
type: string
|
||||
organizationID:
|
||||
$ref: '#/definitions/domain.ValidInt64'
|
||||
otp:
|
||||
language_challange:
|
||||
type: string
|
||||
otpMedium:
|
||||
language_goal:
|
||||
type: string
|
||||
last_name:
|
||||
type: string
|
||||
learning_goal:
|
||||
type: string
|
||||
nick_name:
|
||||
type: string
|
||||
occupation:
|
||||
type: string
|
||||
otp_medium:
|
||||
$ref: '#/definitions/domain.OtpMedium'
|
||||
password:
|
||||
type: string
|
||||
phoneNumber:
|
||||
phone_number:
|
||||
type: string
|
||||
preferredLanguage:
|
||||
preferred_language:
|
||||
type: string
|
||||
region:
|
||||
type: string
|
||||
role:
|
||||
type: string
|
||||
userName:
|
||||
user_name:
|
||||
type: string
|
||||
type: object
|
||||
domain.ResendOtpReq:
|
||||
properties:
|
||||
user_name:
|
||||
type: string
|
||||
required:
|
||||
- user_name
|
||||
type: object
|
||||
domain.Response:
|
||||
properties:
|
||||
data: {}
|
||||
|
|
@ -122,6 +157,37 @@ definitions:
|
|||
- RoleStudent
|
||||
- RoleInstructor
|
||||
- RoleSupport
|
||||
domain.SubmitAssessmentReq:
|
||||
properties:
|
||||
answers:
|
||||
items:
|
||||
$ref: '#/definitions/domain.UserAnswer'
|
||||
minItems: 1
|
||||
type: array
|
||||
required:
|
||||
- answers
|
||||
type: object
|
||||
domain.UpdateKnowledgeLevelReq:
|
||||
properties:
|
||||
knowledge_level:
|
||||
description: BEGINNER, INTERMEDIATE, ADVANCED
|
||||
type: string
|
||||
user_id:
|
||||
type: integer
|
||||
type: object
|
||||
domain.UserAnswer:
|
||||
properties:
|
||||
isCorrect:
|
||||
type: boolean
|
||||
questionID:
|
||||
format: int64
|
||||
type: integer
|
||||
selectedOptionID:
|
||||
format: int64
|
||||
type: integer
|
||||
shortAnswer:
|
||||
type: string
|
||||
type: object
|
||||
domain.UserProfileResponse:
|
||||
properties:
|
||||
age:
|
||||
|
|
@ -136,16 +202,29 @@ definitions:
|
|||
type: string
|
||||
email_verified:
|
||||
type: boolean
|
||||
favoutite_topic:
|
||||
type: string
|
||||
first_name:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
initial_assessment_completed:
|
||||
description: Profile fields
|
||||
type: boolean
|
||||
language_challange:
|
||||
type: string
|
||||
language_goal:
|
||||
type: string
|
||||
last_login:
|
||||
type: string
|
||||
last_name:
|
||||
type: string
|
||||
organization_id:
|
||||
type: integer
|
||||
learning_goal:
|
||||
type: string
|
||||
nick_name:
|
||||
type: string
|
||||
occupation:
|
||||
type: string
|
||||
phone_number:
|
||||
type: string
|
||||
phone_verified:
|
||||
|
|
@ -179,35 +258,15 @@ definitions:
|
|||
- UserStatusActive
|
||||
- UserStatusSuspended
|
||||
- UserStatusDeactivated
|
||||
domain.ValidInt64:
|
||||
properties:
|
||||
valid:
|
||||
type: boolean
|
||||
value:
|
||||
type: integer
|
||||
type: object
|
||||
domain.VerifyOtpReq:
|
||||
properties:
|
||||
email:
|
||||
description: Required if medium is email
|
||||
type: string
|
||||
otp:
|
||||
type: string
|
||||
otp_for:
|
||||
$ref: '#/definitions/domain.OtpFor'
|
||||
otp_medium:
|
||||
allOf:
|
||||
- $ref: '#/definitions/domain.OtpMedium'
|
||||
enum:
|
||||
- email
|
||||
- sms
|
||||
phone_number:
|
||||
description: Required if medium is SMS
|
||||
user_name:
|
||||
type: string
|
||||
required:
|
||||
- otp
|
||||
- otp_for
|
||||
- otp_medium
|
||||
- user_name
|
||||
type: object
|
||||
handlers.AdminProfileRes:
|
||||
properties:
|
||||
|
|
@ -285,9 +344,6 @@ definitions:
|
|||
type: object
|
||||
handlers.CreateAdminReq:
|
||||
properties:
|
||||
company_id:
|
||||
example: 1
|
||||
type: integer
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
type: string
|
||||
|
|
@ -304,37 +360,6 @@ definitions:
|
|||
example: "1234567890"
|
||||
type: string
|
||||
type: object
|
||||
handlers.CustomerProfileRes:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
email_verified:
|
||||
type: boolean
|
||||
first_name:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
last_login:
|
||||
type: string
|
||||
last_name:
|
||||
type: string
|
||||
phone_number:
|
||||
type: string
|
||||
phone_verified:
|
||||
type: boolean
|
||||
referral_code:
|
||||
type: string
|
||||
role:
|
||||
$ref: '#/definitions/domain.Role'
|
||||
suspended:
|
||||
type: boolean
|
||||
suspended_at:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
handlers.LoginAdminRes:
|
||||
properties:
|
||||
access_token:
|
||||
|
|
@ -364,9 +389,6 @@ definitions:
|
|||
type: object
|
||||
handlers.ResetPasswordReq:
|
||||
properties:
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
type: string
|
||||
otp:
|
||||
example: "123456"
|
||||
type: string
|
||||
|
|
@ -374,12 +396,13 @@ definitions:
|
|||
example: newpassword123
|
||||
minLength: 8
|
||||
type: string
|
||||
phone_number:
|
||||
example: "1234567890"
|
||||
user_name:
|
||||
example: johndoe
|
||||
type: string
|
||||
required:
|
||||
- otp
|
||||
- password
|
||||
- user_name
|
||||
type: object
|
||||
handlers.SearchUserByNameOrPhoneReq:
|
||||
properties:
|
||||
|
|
@ -388,21 +411,31 @@ definitions:
|
|||
role:
|
||||
$ref: '#/definitions/domain.Role'
|
||||
type: object
|
||||
handlers.SendSingleAfroSMSReq:
|
||||
properties:
|
||||
message:
|
||||
example: Hello world
|
||||
type: string
|
||||
recipient:
|
||||
example: "+251912345678"
|
||||
type: string
|
||||
required:
|
||||
- message
|
||||
- recipient
|
||||
type: object
|
||||
handlers.loginAdminReq:
|
||||
properties:
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
type: string
|
||||
password:
|
||||
example: password123
|
||||
type: string
|
||||
phone_number:
|
||||
example: "1234567890"
|
||||
user_name:
|
||||
example: adminuser
|
||||
type: string
|
||||
required:
|
||||
- password
|
||||
- user_name
|
||||
type: object
|
||||
handlers.loginCustomerReq:
|
||||
handlers.loginUserReq:
|
||||
properties:
|
||||
password:
|
||||
example: password123
|
||||
|
|
@ -414,7 +447,7 @@ definitions:
|
|||
- password
|
||||
- user_name
|
||||
type: object
|
||||
handlers.loginCustomerRes:
|
||||
handlers.loginUserRes:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
|
|
@ -445,9 +478,6 @@ definitions:
|
|||
type: object
|
||||
handlers.updateAdminReq:
|
||||
properties:
|
||||
company_id:
|
||||
example: 1
|
||||
type: integer
|
||||
first_name:
|
||||
example: John
|
||||
type: string
|
||||
|
|
@ -498,7 +528,7 @@ paths:
|
|||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Login customer
|
||||
description: Login user
|
||||
parameters:
|
||||
- description: Login admin
|
||||
in: body
|
||||
|
|
@ -525,28 +555,98 @@ paths:
|
|||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Login customer
|
||||
summary: Login user
|
||||
tags:
|
||||
- auth
|
||||
/api/v1/{tenant_slug}/customer-login:
|
||||
/api/v1/{tenant_slug}/assessment/submit:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Login customer
|
||||
description: Evaluates user responses, calculates knowledge level, updates user
|
||||
profile, and sends notification
|
||||
parameters:
|
||||
- description: Login customer
|
||||
- description: User ID
|
||||
in: path
|
||||
name: user_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: Assessment responses
|
||||
in: body
|
||||
name: login
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.loginCustomerReq'
|
||||
$ref: '#/definitions/domain.SubmitAssessmentReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.loginCustomerRes'
|
||||
$ref: '#/definitions/domain.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
summary: Submit initial knowledge assessment
|
||||
tags:
|
||||
- assessment
|
||||
/api/v1/{tenant_slug}/otp/resend:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Resend OTP if the previous one is expired
|
||||
parameters:
|
||||
- description: Resend OTP
|
||||
in: body
|
||||
name: resendOtp
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ResendOtpReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Resend OTP
|
||||
tags:
|
||||
- otp
|
||||
/api/v1/{tenant_slug}/user-login:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Login user
|
||||
parameters:
|
||||
- description: Login user
|
||||
in: body
|
||||
name: login
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.loginUserReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.loginUserRes'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
|
|
@ -559,7 +659,7 @@ paths:
|
|||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Login customer
|
||||
summary: Login user
|
||||
tags:
|
||||
- auth
|
||||
/api/v1/{tenant_slug}/user/{user_name}/is-pending:
|
||||
|
|
@ -650,29 +750,44 @@ paths:
|
|||
summary: Check if phone number or email exist
|
||||
tags:
|
||||
- user
|
||||
/api/v1/{tenant_slug}/user/customer-profile:
|
||||
get:
|
||||
/api/v1/{tenant_slug}/user/knowledge-level:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Get user profile
|
||||
description: Updates the knowledge level of the specified user after initial
|
||||
assessment
|
||||
parameters:
|
||||
- description: User ID
|
||||
in: path
|
||||
name: user_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: Knowledge level
|
||||
in: body
|
||||
name: knowledge_level
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/domain.UpdateKnowledgeLevelReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.CustomerProfileRes'
|
||||
$ref: '#/definitions/domain.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get user profile
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
summary: Update user's knowledge level
|
||||
tags:
|
||||
- user
|
||||
/api/v1/{tenant_slug}/user/register:
|
||||
|
|
@ -795,25 +910,18 @@ paths:
|
|||
summary: Send reset code
|
||||
tags:
|
||||
- user
|
||||
/api/v1/{tenant_slug}/user/verify-otp:
|
||||
post:
|
||||
/api/v1/{tenant_slug}/user/user-profile:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Verify OTP for registration or other actions
|
||||
parameters:
|
||||
- description: Verify OTP
|
||||
in: body
|
||||
name: verifyOtp
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/domain.VerifyOtpReq'
|
||||
description: Get user profile
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
$ref: '#/definitions/domain.UserProfileResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
|
|
@ -822,7 +930,9 @@ paths:
|
|||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Verify OTP
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get user profile
|
||||
tags:
|
||||
- user
|
||||
/api/v1/admin:
|
||||
|
|
@ -960,13 +1070,73 @@ paths:
|
|||
summary: Update Admin
|
||||
tags:
|
||||
- admin
|
||||
/api/v1/assessment/questions:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns all active questions used for initial knowledge assessment
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/domain.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/domain.AssessmentQuestion'
|
||||
type: array
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
summary: Get active initial assessment questions
|
||||
tags:
|
||||
- assessment
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Creates a new question for the initial knowledge assessment
|
||||
parameters:
|
||||
- description: Assessment question payload
|
||||
in: body
|
||||
name: question
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/domain.AssessmentQuestion'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/domain.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/domain.AssessmentQuestion'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
summary: Create assessment question
|
||||
tags:
|
||||
- assessment
|
||||
/api/v1/auth/logout:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Logout customer
|
||||
description: Logout user
|
||||
parameters:
|
||||
- description: Logout customer
|
||||
- description: Logout user
|
||||
in: body
|
||||
name: logout
|
||||
required: true
|
||||
|
|
@ -991,7 +1161,7 @@ paths:
|
|||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Logout customer
|
||||
summary: Logout user
|
||||
tags:
|
||||
- auth
|
||||
/api/v1/auth/refresh:
|
||||
|
|
@ -1012,7 +1182,7 @@ paths:
|
|||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.loginCustomerRes'
|
||||
$ref: '#/definitions/handlers.loginUserRes'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
|
|
@ -1070,6 +1240,36 @@ paths:
|
|||
summary: Retrieve application logs with filtering and pagination
|
||||
tags:
|
||||
- Logs
|
||||
/api/v1/sendSMS:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Sends an SMS message to a single phone number using AfroMessage
|
||||
parameters:
|
||||
- description: Send SMS request
|
||||
in: body
|
||||
name: sendSMS
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.SendSingleAfroSMSReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Send single SMS via AfroMessage
|
||||
tags:
|
||||
- user
|
||||
/api/v1/super-login:
|
||||
post:
|
||||
consumes:
|
||||
|
|
@ -1381,6 +1581,36 @@ paths:
|
|||
summary: Get user by id
|
||||
tags:
|
||||
- user
|
||||
/api/v1/user/verify-otp:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Verify OTP for registration or other actions
|
||||
parameters:
|
||||
- description: Verify OTP
|
||||
in: body
|
||||
name: verifyOtp
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/domain.VerifyOtpReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Verify OTP
|
||||
tags:
|
||||
- user
|
||||
securityDefinitions:
|
||||
Bearer:
|
||||
in: header
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
// source: auth.sql
|
||||
|
||||
package dbgen
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
|
||||
package dbgen
|
||||
|
||||
|
|
|
|||
290
gen/db/initial_assessment.sql.go
Normal file
290
gen/db/initial_assessment.sql.go
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: initial_assessment.sql
|
||||
|
||||
package dbgen
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const CreateAssessmentAnswer = `-- name: CreateAssessmentAnswer :exec
|
||||
INSERT INTO assessment_answers (
|
||||
attempt_id,
|
||||
question_id,
|
||||
selected_option_id,
|
||||
short_answer,
|
||||
is_correct
|
||||
)
|
||||
VALUES (
|
||||
$1, -- attempt_id
|
||||
$2, -- question_id
|
||||
$3, -- selected_option_id
|
||||
$4, -- short_answer
|
||||
$5 -- is_correct
|
||||
)
|
||||
`
|
||||
|
||||
type CreateAssessmentAnswerParams struct {
|
||||
AttemptID int64 `json:"attempt_id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
SelectedOptionID pgtype.Int8 `json:"selected_option_id"`
|
||||
ShortAnswer pgtype.Text `json:"short_answer"`
|
||||
IsCorrect bool `json:"is_correct"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAssessmentAnswer(ctx context.Context, arg CreateAssessmentAnswerParams) error {
|
||||
_, err := q.db.Exec(ctx, CreateAssessmentAnswer,
|
||||
arg.AttemptID,
|
||||
arg.QuestionID,
|
||||
arg.SelectedOptionID,
|
||||
arg.ShortAnswer,
|
||||
arg.IsCorrect,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const CreateAssessmentAttempt = `-- name: CreateAssessmentAttempt :one
|
||||
INSERT INTO assessment_attempts (
|
||||
user_id,
|
||||
total_questions,
|
||||
correct_answers,
|
||||
score_percentage,
|
||||
knowledge_level
|
||||
)
|
||||
VALUES (
|
||||
$1, -- user_id
|
||||
$2, -- total_questions
|
||||
$3, -- correct_answers
|
||||
$4, -- score_percentage
|
||||
$5 -- knowledge_level
|
||||
)
|
||||
RETURNING
|
||||
id,
|
||||
user_id,
|
||||
total_questions,
|
||||
correct_answers,
|
||||
score_percentage,
|
||||
knowledge_level,
|
||||
completed_at
|
||||
`
|
||||
|
||||
type CreateAssessmentAttemptParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
TotalQuestions int32 `json:"total_questions"`
|
||||
CorrectAnswers int32 `json:"correct_answers"`
|
||||
ScorePercentage pgtype.Numeric `json:"score_percentage"`
|
||||
KnowledgeLevel string `json:"knowledge_level"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAssessmentAttempt(ctx context.Context, arg CreateAssessmentAttemptParams) (AssessmentAttempt, error) {
|
||||
row := q.db.QueryRow(ctx, CreateAssessmentAttempt,
|
||||
arg.UserID,
|
||||
arg.TotalQuestions,
|
||||
arg.CorrectAnswers,
|
||||
arg.ScorePercentage,
|
||||
arg.KnowledgeLevel,
|
||||
)
|
||||
var i AssessmentAttempt
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.TotalQuestions,
|
||||
&i.CorrectAnswers,
|
||||
&i.ScorePercentage,
|
||||
&i.KnowledgeLevel,
|
||||
&i.CompletedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const CreateAssessmentQuestion = `-- name: CreateAssessmentQuestion :one
|
||||
INSERT INTO assessment_questions (
|
||||
title,
|
||||
description,
|
||||
question_type,
|
||||
difficulty_level
|
||||
)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, title, description, question_type, difficulty_level, is_active, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateAssessmentQuestionParams struct {
|
||||
Title string `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
QuestionType string `json:"question_type"`
|
||||
DifficultyLevel string `json:"difficulty_level"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAssessmentQuestion(ctx context.Context, arg CreateAssessmentQuestionParams) (AssessmentQuestion, error) {
|
||||
row := q.db.QueryRow(ctx, CreateAssessmentQuestion,
|
||||
arg.Title,
|
||||
arg.Description,
|
||||
arg.QuestionType,
|
||||
arg.DifficultyLevel,
|
||||
)
|
||||
var i AssessmentQuestion
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Title,
|
||||
&i.Description,
|
||||
&i.QuestionType,
|
||||
&i.DifficultyLevel,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const CreateAssessmentQuestionOption = `-- name: CreateAssessmentQuestionOption :exec
|
||||
INSERT INTO assessment_question_options (
|
||||
question_id,
|
||||
option_text,
|
||||
is_correct
|
||||
)
|
||||
VALUES ($1, $2, $3)
|
||||
`
|
||||
|
||||
type CreateAssessmentQuestionOptionParams struct {
|
||||
QuestionID int64 `json:"question_id"`
|
||||
OptionText string `json:"option_text"`
|
||||
IsCorrect bool `json:"is_correct"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAssessmentQuestionOption(ctx context.Context, arg CreateAssessmentQuestionOptionParams) error {
|
||||
_, err := q.db.Exec(ctx, CreateAssessmentQuestionOption, arg.QuestionID, arg.OptionText, arg.IsCorrect)
|
||||
return err
|
||||
}
|
||||
|
||||
const GetActiveAssessmentQuestions = `-- name: GetActiveAssessmentQuestions :many
|
||||
SELECT id, title, description, question_type, difficulty_level, is_active, created_at, updated_at
|
||||
FROM assessment_questions
|
||||
WHERE is_active = TRUE
|
||||
ORDER BY difficulty_level, id
|
||||
`
|
||||
|
||||
func (q *Queries) GetActiveAssessmentQuestions(ctx context.Context) ([]AssessmentQuestion, error) {
|
||||
rows, err := q.db.Query(ctx, GetActiveAssessmentQuestions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []AssessmentQuestion
|
||||
for rows.Next() {
|
||||
var i AssessmentQuestion
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Title,
|
||||
&i.Description,
|
||||
&i.QuestionType,
|
||||
&i.DifficultyLevel,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const GetAssessmentOptionByID = `-- name: GetAssessmentOptionByID :one
|
||||
SELECT
|
||||
id,
|
||||
question_id,
|
||||
option_text,
|
||||
is_correct
|
||||
FROM assessment_question_options
|
||||
WHERE id = $1
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAssessmentOptionByID(ctx context.Context, id int64) (AssessmentQuestionOption, error) {
|
||||
row := q.db.QueryRow(ctx, GetAssessmentOptionByID, id)
|
||||
var i AssessmentQuestionOption
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.QuestionID,
|
||||
&i.OptionText,
|
||||
&i.IsCorrect,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetCorrectOptionForQuestion = `-- name: GetCorrectOptionForQuestion :one
|
||||
SELECT
|
||||
id
|
||||
FROM assessment_question_options
|
||||
WHERE question_id = $1
|
||||
AND is_correct = TRUE
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCorrectOptionForQuestion(ctx context.Context, questionID int64) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, GetCorrectOptionForQuestion, questionID)
|
||||
var id int64
|
||||
err := row.Scan(&id)
|
||||
return id, err
|
||||
}
|
||||
|
||||
const GetLatestAssessmentAttempt = `-- name: GetLatestAssessmentAttempt :one
|
||||
SELECT id, user_id, total_questions, correct_answers, score_percentage, knowledge_level, completed_at
|
||||
FROM assessment_attempts
|
||||
WHERE user_id = $1
|
||||
ORDER BY completed_at DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetLatestAssessmentAttempt(ctx context.Context, userID int64) (AssessmentAttempt, error) {
|
||||
row := q.db.QueryRow(ctx, GetLatestAssessmentAttempt, userID)
|
||||
var i AssessmentAttempt
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.TotalQuestions,
|
||||
&i.CorrectAnswers,
|
||||
&i.ScorePercentage,
|
||||
&i.KnowledgeLevel,
|
||||
&i.CompletedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetQuestionOptions = `-- name: GetQuestionOptions :many
|
||||
SELECT id, question_id, option_text, is_correct
|
||||
FROM assessment_question_options
|
||||
WHERE question_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetQuestionOptions(ctx context.Context, questionID int64) ([]AssessmentQuestionOption, error) {
|
||||
rows, err := q.db.Query(ctx, GetQuestionOptions, questionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []AssessmentQuestionOption
|
||||
for rows.Next() {
|
||||
var i AssessmentQuestionOption
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.QuestionID,
|
||||
&i.OptionText,
|
||||
&i.IsCorrect,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
// source: issue_reporting.sql
|
||||
|
||||
package dbgen
|
||||
|
|
|
|||
107
gen/db/models.go
107
gen/db/models.go
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
|
||||
package dbgen
|
||||
|
||||
|
|
@ -18,6 +18,43 @@ type Assessment struct {
|
|||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type AssessmentAnswer struct {
|
||||
ID int64 `json:"id"`
|
||||
AttemptID int64 `json:"attempt_id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
SelectedOptionID pgtype.Int8 `json:"selected_option_id"`
|
||||
ShortAnswer pgtype.Text `json:"short_answer"`
|
||||
IsCorrect bool `json:"is_correct"`
|
||||
}
|
||||
|
||||
type AssessmentAttempt struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
TotalQuestions int32 `json:"total_questions"`
|
||||
CorrectAnswers int32 `json:"correct_answers"`
|
||||
ScorePercentage pgtype.Numeric `json:"score_percentage"`
|
||||
KnowledgeLevel string `json:"knowledge_level"`
|
||||
CompletedAt pgtype.Timestamptz `json:"completed_at"`
|
||||
}
|
||||
|
||||
type AssessmentQuestion struct {
|
||||
ID int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
QuestionType string `json:"question_type"`
|
||||
DifficultyLevel string `json:"difficulty_level"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type AssessmentQuestionOption struct {
|
||||
ID int64 `json:"id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
OptionText string `json:"option_text"`
|
||||
IsCorrect bool `json:"is_correct"`
|
||||
}
|
||||
|
||||
type AssessmentSubmission struct {
|
||||
ID int64 `json:"id"`
|
||||
AssessmentID int64 `json:"assessment_id"`
|
||||
|
|
@ -29,15 +66,15 @@ type AssessmentSubmission struct {
|
|||
}
|
||||
|
||||
type Course struct {
|
||||
ID int64 `json:"id"`
|
||||
InstructorID int64 `json:"instructor_id"`
|
||||
Title string `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Level pgtype.Text `json:"level"`
|
||||
Language pgtype.Text `json:"language"`
|
||||
IsPublished bool `json:"is_published"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
ID int64 `json:"id"`
|
||||
InstructorID int64 `json:"instructor_id"`
|
||||
Title string `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Level pgtype.Text `json:"level"`
|
||||
Language pgtype.Text `json:"language"`
|
||||
IsPublished bool `json:"is_published"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type CourseModule struct {
|
||||
|
|
@ -132,25 +169,33 @@ type ReportedIssue struct {
|
|||
}
|
||||
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Password []byte `json:"password"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
LastLogin pgtype.Timestamptz `json:"last_login"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Password []byte `json:"password"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
KnowledgeLevel pgtype.Text `json:"knowledge_level"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
LastLogin pgtype.Timestamptz `json:"last_login"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
// source: notification.sql
|
||||
|
||||
package dbgen
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
// source: otp.sql
|
||||
|
||||
package dbgen
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
// source: settings.sql
|
||||
|
||||
package dbgen
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// sqlc v1.30.0
|
||||
// source: user.sql
|
||||
|
||||
package dbgen
|
||||
|
|
@ -14,14 +14,10 @@ import (
|
|||
const CheckPhoneEmailExist = `-- name: CheckPhoneEmailExist :one
|
||||
SELECT
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM users u1
|
||||
WHERE u1.phone_number = $1
|
||||
SELECT 1 FROM users u1 WHERE u1.phone_number = $1
|
||||
) AS phone_exists,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM users u2
|
||||
WHERE u2.email = $2
|
||||
SELECT 1 FROM users u2 WHERE u2.email = $2
|
||||
) AS email_exists
|
||||
`
|
||||
|
||||
|
|
@ -55,30 +51,50 @@ INSERT INTO users (
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
profile_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
updated_at
|
||||
)
|
||||
VALUES (
|
||||
$1, -- first_name
|
||||
$2, -- last_name
|
||||
$3, -- user_name
|
||||
$4, -- email
|
||||
$5, -- phone_number
|
||||
$6, -- role
|
||||
$7, -- password (BYTEA)
|
||||
$8, -- age
|
||||
$9, -- education_level
|
||||
$10, -- country
|
||||
$11, -- region
|
||||
$12, -- email_verified
|
||||
$13, -- phone_verified
|
||||
$14, -- status (PENDING | ACTIVE)
|
||||
$15, -- profile_completed
|
||||
$16, -- preferred_language
|
||||
$1, -- first_name
|
||||
$2, -- last_name
|
||||
$3, -- user_name
|
||||
$4, -- email
|
||||
$5, -- phone_number
|
||||
$6, -- role
|
||||
$7, -- password
|
||||
$8, -- age
|
||||
$9, -- education_level
|
||||
$10, -- country
|
||||
$11, -- region
|
||||
|
||||
$12, -- nick_name
|
||||
$13, -- occupation
|
||||
$14, -- learning_goal
|
||||
$15, -- language_goal
|
||||
$16, -- language_challange
|
||||
$17, -- favoutite_topic
|
||||
|
||||
$18, -- initial_assessment_completed
|
||||
$19, -- email_verified
|
||||
$20, -- phone_verified
|
||||
$21, -- status
|
||||
$22, -- profile_completed
|
||||
$23, -- profile_picture_url
|
||||
$24, -- preferred_language
|
||||
CURRENT_TIMESTAMP
|
||||
)
|
||||
RETURNING
|
||||
|
|
@ -93,53 +109,79 @@ RETURNING
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
profile_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
created_at,
|
||||
updated_at
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Password []byte `json:"password"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Password []byte `json:"password"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
}
|
||||
|
||||
type CreateUserRow struct {
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
|
||||
|
|
@ -155,10 +197,18 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateU
|
|||
arg.EducationLevel,
|
||||
arg.Country,
|
||||
arg.Region,
|
||||
arg.NickName,
|
||||
arg.Occupation,
|
||||
arg.LearningGoal,
|
||||
arg.LanguageGoal,
|
||||
arg.LanguageChallange,
|
||||
arg.FavoutiteTopic,
|
||||
arg.InitialAssessmentCompleted,
|
||||
arg.EmailVerified,
|
||||
arg.PhoneVerified,
|
||||
arg.Status,
|
||||
arg.ProfileCompleted,
|
||||
arg.ProfilePictureUrl,
|
||||
arg.PreferredLanguage,
|
||||
)
|
||||
var i CreateUserRow
|
||||
|
|
@ -174,10 +224,18 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateU
|
|||
&i.EducationLevel,
|
||||
&i.Country,
|
||||
&i.Region,
|
||||
&i.NickName,
|
||||
&i.Occupation,
|
||||
&i.LearningGoal,
|
||||
&i.LanguageGoal,
|
||||
&i.LanguageChallange,
|
||||
&i.FavoutiteTopic,
|
||||
&i.InitialAssessmentCompleted,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.Status,
|
||||
&i.ProfileCompleted,
|
||||
&i.ProfilePictureUrl,
|
||||
&i.PreferredLanguage,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
|
|
@ -209,6 +267,17 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -249,25 +318,34 @@ type GetAllUsersParams struct {
|
|||
}
|
||||
|
||||
type GetAllUsersRow struct {
|
||||
TotalCount int64 `json:"total_count"`
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
PreferredLanguage_2 pgtype.Text `json:"preferred_language_2"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]GetAllUsersRow, error) {
|
||||
|
|
@ -299,11 +377,20 @@ func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]Get
|
|||
&i.EducationLevel,
|
||||
&i.Country,
|
||||
&i.Region,
|
||||
&i.NickName,
|
||||
&i.Occupation,
|
||||
&i.LearningGoal,
|
||||
&i.LanguageGoal,
|
||||
&i.LanguageChallange,
|
||||
&i.FavoutiteTopic,
|
||||
&i.InitialAssessmentCompleted,
|
||||
&i.ProfilePictureUrl,
|
||||
&i.PreferredLanguage,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.Status,
|
||||
&i.ProfileCompleted,
|
||||
&i.PreferredLanguage,
|
||||
&i.PreferredLanguage_2,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
|
@ -344,6 +431,14 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -355,7 +450,7 @@ SELECT
|
|||
updated_at
|
||||
FROM users
|
||||
WHERE (email = $1 AND $1 IS NOT NULL)
|
||||
OR (phone_number = $2 AND $2 IS NOT NULL)
|
||||
OR (phone_number = $2 AND $2 IS NOT NULL)
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
|
|
@ -377,6 +472,12 @@ type GetUserByEmailPhoneRow struct {
|
|||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
|
|
@ -404,6 +505,12 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho
|
|||
&i.EducationLevel,
|
||||
&i.Country,
|
||||
&i.Region,
|
||||
&i.NickName,
|
||||
&i.Occupation,
|
||||
&i.LearningGoal,
|
||||
&i.LanguageGoal,
|
||||
&i.LanguageChallange,
|
||||
&i.FavoutiteTopic,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.Status,
|
||||
|
|
@ -418,7 +525,7 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho
|
|||
}
|
||||
|
||||
const GetUserByID = `-- name: GetUserByID :one
|
||||
SELECT id, first_name, last_name, user_name, email, phone_number, role, password, age, education_level, country, region, email_verified, phone_verified, status, last_login, profile_completed, profile_picture_url, preferred_language, created_at, updated_at
|
||||
SELECT id, first_name, last_name, user_name, email, phone_number, role, password, age, education_level, country, region, knowledge_level, nick_name, occupation, learning_goal, language_goal, language_challange, favoutite_topic, initial_assessment_completed, email_verified, phone_verified, status, last_login, profile_completed, profile_picture_url, preferred_language, created_at, updated_at
|
||||
FROM users
|
||||
WHERE id = $1
|
||||
`
|
||||
|
|
@ -439,6 +546,14 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
|||
&i.EducationLevel,
|
||||
&i.Country,
|
||||
&i.Region,
|
||||
&i.KnowledgeLevel,
|
||||
&i.NickName,
|
||||
&i.Occupation,
|
||||
&i.LearningGoal,
|
||||
&i.LanguageGoal,
|
||||
&i.LanguageChallange,
|
||||
&i.FavoutiteTopic,
|
||||
&i.InitialAssessmentCompleted,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.Status,
|
||||
|
|
@ -466,6 +581,14 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -493,6 +616,12 @@ type GetUserByUserNameRow struct {
|
|||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
|
|
@ -520,6 +649,12 @@ func (q *Queries) GetUserByUserName(ctx context.Context, userName string) (GetUs
|
|||
&i.EducationLevel,
|
||||
&i.Country,
|
||||
&i.Region,
|
||||
&i.NickName,
|
||||
&i.Occupation,
|
||||
&i.LearningGoal,
|
||||
&i.LanguageGoal,
|
||||
&i.LanguageChallange,
|
||||
&i.FavoutiteTopic,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.Status,
|
||||
|
|
@ -575,6 +710,17 @@ SELECT
|
|||
education_level,
|
||||
country,
|
||||
region,
|
||||
|
||||
nick_name,
|
||||
occupation,
|
||||
learning_goal,
|
||||
language_goal,
|
||||
language_challange,
|
||||
favoutite_topic,
|
||||
|
||||
initial_assessment_completed,
|
||||
profile_picture_url,
|
||||
preferred_language,
|
||||
email_verified,
|
||||
phone_verified,
|
||||
status,
|
||||
|
|
@ -600,23 +746,32 @@ type SearchUserByNameOrPhoneParams struct {
|
|||
}
|
||||
|
||||
type SearchUserByNameOrPhoneRow struct {
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Role string `json:"role"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByNameOrPhoneParams) ([]SearchUserByNameOrPhoneRow, error) {
|
||||
|
|
@ -640,6 +795,15 @@ func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByN
|
|||
&i.EducationLevel,
|
||||
&i.Country,
|
||||
&i.Region,
|
||||
&i.NickName,
|
||||
&i.Occupation,
|
||||
&i.LearningGoal,
|
||||
&i.LanguageGoal,
|
||||
&i.LanguageChallange,
|
||||
&i.FavoutiteTopic,
|
||||
&i.InitialAssessmentCompleted,
|
||||
&i.ProfilePictureUrl,
|
||||
&i.PreferredLanguage,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.Status,
|
||||
|
|
@ -662,47 +826,117 @@ UPDATE users
|
|||
SET
|
||||
password = $1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = $2 OR phone_number = $3
|
||||
WHERE user_name = $2
|
||||
`
|
||||
|
||||
type UpdatePasswordParams struct {
|
||||
Password []byte `json:"password"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
Password []byte `json:"password"`
|
||||
UserName string `json:"user_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
|
||||
_, err := q.db.Exec(ctx, UpdatePassword, arg.Password, arg.Email, arg.PhoneNumber)
|
||||
_, err := q.db.Exec(ctx, UpdatePassword, arg.Password, arg.UserName)
|
||||
return err
|
||||
}
|
||||
|
||||
const UpdateUser = `-- name: UpdateUser :exec
|
||||
UPDATE users
|
||||
SET
|
||||
first_name = $1,
|
||||
last_name = $2,
|
||||
status = $3,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $4
|
||||
first_name = $1,
|
||||
last_name = $2,
|
||||
user_name = $3,
|
||||
age = $4,
|
||||
education_level = $5,
|
||||
country = $6,
|
||||
region = $7,
|
||||
|
||||
nick_name = $8,
|
||||
occupation = $9,
|
||||
learning_goal = $10,
|
||||
language_goal = $11,
|
||||
language_challange = $12,
|
||||
favoutite_topic = $13,
|
||||
|
||||
initial_assessment_completed = $14,
|
||||
email_verified = $15,
|
||||
phone_verified = $16,
|
||||
status = $17,
|
||||
profile_completed = $18,
|
||||
profile_picture_url = $19,
|
||||
preferred_language = $20,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $21
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Status string `json:"status"`
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Age pgtype.Int4 `json:"age"`
|
||||
EducationLevel pgtype.Text `json:"education_level"`
|
||||
Country pgtype.Text `json:"country"`
|
||||
Region pgtype.Text `json:"region"`
|
||||
NickName pgtype.Text `json:"nick_name"`
|
||||
Occupation pgtype.Text `json:"occupation"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status string `json:"status"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
|
||||
_, err := q.db.Exec(ctx, UpdateUser,
|
||||
arg.FirstName,
|
||||
arg.LastName,
|
||||
arg.UserName,
|
||||
arg.Age,
|
||||
arg.EducationLevel,
|
||||
arg.Country,
|
||||
arg.Region,
|
||||
arg.NickName,
|
||||
arg.Occupation,
|
||||
arg.LearningGoal,
|
||||
arg.LanguageGoal,
|
||||
arg.LanguageChallange,
|
||||
arg.FavoutiteTopic,
|
||||
arg.InitialAssessmentCompleted,
|
||||
arg.EmailVerified,
|
||||
arg.PhoneVerified,
|
||||
arg.Status,
|
||||
arg.ProfileCompleted,
|
||||
arg.ProfilePictureUrl,
|
||||
arg.PreferredLanguage,
|
||||
arg.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const UpdateUserKnowledgeLevel = `-- name: UpdateUserKnowledgeLevel :exec
|
||||
UPDATE users
|
||||
SET
|
||||
knowledge_level = $1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2
|
||||
`
|
||||
|
||||
type UpdateUserKnowledgeLevelParams struct {
|
||||
KnowledgeLevel pgtype.Text `json:"knowledge_level"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserKnowledgeLevel(ctx context.Context, arg UpdateUserKnowledgeLevelParams) error {
|
||||
_, err := q.db.Exec(ctx, UpdateUserKnowledgeLevel, arg.KnowledgeLevel, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const UpdateUserStatus = `-- name: UpdateUserStatus :exec
|
||||
UPDATE users
|
||||
SET
|
||||
|
|
|
|||
47
internal/domain/initial_assessment.go
Normal file
47
internal/domain/initial_assessment.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type QuestionType string
|
||||
|
||||
const (
|
||||
QuestionTypeMultipleChoice QuestionType = "multiple_choice"
|
||||
QuestionTypeTrueFalse QuestionType = "true_false"
|
||||
QuestionTypeShortAnswer QuestionType = "short_answer"
|
||||
)
|
||||
|
||||
type SubmitAssessmentReq struct {
|
||||
Answers []UserAnswer `json:"answers" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type AssessmentQuestion struct {
|
||||
ID int64
|
||||
Title string
|
||||
Description string
|
||||
QuestionType string
|
||||
DifficultyLevel string
|
||||
Options []AssessmentOption
|
||||
}
|
||||
|
||||
type AssessmentOption struct {
|
||||
ID int64
|
||||
OptionText string
|
||||
IsCorrect bool
|
||||
}
|
||||
|
||||
type UserAnswer struct {
|
||||
QuestionID int64
|
||||
SelectedOptionID int64
|
||||
ShortAnswer string
|
||||
IsCorrect bool
|
||||
}
|
||||
|
||||
type AssessmentAttempt struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
TotalQuestions int
|
||||
CorrectAnswers int
|
||||
ScorePercentage float64
|
||||
KnowledgeLevel string
|
||||
CompletedAt time.Time
|
||||
}
|
||||
|
|
@ -14,27 +14,7 @@ type NotificationDeliveryStatus string
|
|||
type DeliveryChannel string
|
||||
|
||||
const (
|
||||
NotificationTypeWalletUpdated NotificationType = "wallet_updated"
|
||||
NotificationTypeDepositResult NotificationType = "deposit_result"
|
||||
NotificationTypeDepositVerification NotificationType = "deposit_verification"
|
||||
NotificationTypeCashOutSuccess NotificationType = "cash_out_success"
|
||||
NotificationTypeDepositSuccess NotificationType = "deposit_success"
|
||||
NotificationTypeWithdrawSuccess NotificationType = "withdraw_success"
|
||||
NotificationTypeBetPlaced NotificationType = "bet_placed"
|
||||
NotificationTypeDailyReport NotificationType = "daily_report"
|
||||
NotificationTypeReportRequest NotificationType = "report_request"
|
||||
NotificationTypeHighLossOnBet NotificationType = "high_loss_on_bet"
|
||||
NotificationTypeBetOverload NotificationType = "bet_overload"
|
||||
NotificationTypeSignUpWelcome NotificationType = "signup_welcome"
|
||||
NotificationTypeOTPSent NotificationType = "otp_sent"
|
||||
NOTIFICATION_TYPE_WALLET NotificationType = "wallet_threshold"
|
||||
NOTIFICATION_TYPE_TRANSFER_FAIL NotificationType = "transfer_failed"
|
||||
NOTIFICATION_TYPE_TRANSFER_SUCCESS NotificationType = "transfer_success"
|
||||
NOTIFICATION_TYPE_ADMIN_ALERT NotificationType = "admin_alert"
|
||||
NOTIFICATION_TYPE_BET_RESULT NotificationType = "bet_result"
|
||||
NOTIFICATION_TYPE_TRANSFER_REJECTED NotificationType = "transfer_rejected"
|
||||
NOTIFICATION_TYPE_APPROVAL_REQUIRED NotificationType = "approval_required"
|
||||
NOTIFICATION_TYPE_BONUS_AWARDED NotificationType = "bonus_awarded"
|
||||
NOTIFICATION_TYPE_KNOWLEDGE_LEVEL_UPDATE NotificationType = "knowledge_level_update"
|
||||
|
||||
NotificationRecieverSideAdmin NotificationRecieverSide = "admin"
|
||||
NotificationRecieverSideCustomer NotificationRecieverSide = "customer"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@ const (
|
|||
UserStatusDeactivated UserStatus = "DEACTIVATED"
|
||||
)
|
||||
|
||||
type UpdateKnowledgeLevelReq struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
KnowledgeLevel string `json:"knowledge_level"` // BEGINNER, INTERMEDIATE, ADVANCED
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64
|
||||
FirstName string
|
||||
|
|
@ -40,6 +45,15 @@ type User struct {
|
|||
Country string
|
||||
Region string
|
||||
|
||||
// Profile fields
|
||||
initial_assessment_completed bool
|
||||
NickName string
|
||||
Occupation string
|
||||
LearningGoal string
|
||||
LanguageGoal string
|
||||
LanguageChallange string
|
||||
FavoutiteTopic string
|
||||
|
||||
EmailVerified bool
|
||||
PhoneVerified bool
|
||||
Status UserStatus
|
||||
|
|
@ -54,26 +68,39 @@ type User struct {
|
|||
}
|
||||
|
||||
type UserProfileResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
PhoneNumber string `json:"phone_number,omitempty"`
|
||||
Role Role `json:"role"`
|
||||
Age int `json:"age,omitempty"`
|
||||
EducationLevel string `json:"education_level,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status UserStatus `json:"status"`
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
PhoneNumber string `json:"phone_number,omitempty"`
|
||||
Role Role `json:"role"`
|
||||
|
||||
Age int `json:"age,omitempty"`
|
||||
EducationLevel string `json:"education_level,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
|
||||
// Profile fields
|
||||
InitialAssessmentCompleted bool `json:"initial_assessment_completed,omitempty"`
|
||||
NickName string `json:"nick_name,omitempty"`
|
||||
Occupation string `json:"occupation,omitempty"`
|
||||
LearningGoal string `json:"learning_goal,omitempty"`
|
||||
LanguageGoal string `json:"language_goal,omitempty"`
|
||||
LanguageChallange string `json:"language_challange,omitempty"`
|
||||
FavoutiteTopic string `json:"favoutite_topic,omitempty"`
|
||||
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
Status UserStatus `json:"status"`
|
||||
|
||||
LastLogin *time.Time `json:"last_login,omitempty"`
|
||||
ProfileCompleted bool `json:"profile_completed"`
|
||||
ProfilePictureURL string `json:"profile_picture_url,omitempty"`
|
||||
PreferredLanguage string `json:"preferred_language,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
type UserFilter struct {
|
||||
|
|
@ -87,21 +114,28 @@ type UserFilter struct {
|
|||
}
|
||||
|
||||
type RegisterUserReq struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
UserName string
|
||||
Email string
|
||||
PhoneNumber string
|
||||
Password string
|
||||
Role string
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
UserName string `json:"user_name"`
|
||||
Email string `json:"email"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
|
||||
OtpMedium OtpMedium
|
||||
OtpMedium OtpMedium `json:"otp_medium"`
|
||||
|
||||
Age int
|
||||
EducationLevel string
|
||||
Country string
|
||||
Region string
|
||||
PreferredLanguage string
|
||||
NickName string `json:"nick_name,omitempty"`
|
||||
Occupation string `json:"occupation,omitempty"`
|
||||
LearningGoal string `json:"learning_goal,omitempty"`
|
||||
LanguageGoal string `json:"language_goal,omitempty"`
|
||||
LanguageChallange string `json:"language_challange,omitempty"`
|
||||
FavoutiteTopic string `json:"favoutite_topic,omitempty"`
|
||||
|
||||
Age int `json:"age,omitempty"`
|
||||
EducationLevel string `json:"education_level,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
PreferredLanguage string `json:"preferred_language,omitempty"`
|
||||
}
|
||||
|
||||
type CreateUserReq struct {
|
||||
|
|
@ -115,10 +149,19 @@ type CreateUserReq struct {
|
|||
|
||||
Status UserStatus
|
||||
|
||||
Age int
|
||||
EducationLevel string
|
||||
Country string
|
||||
Region string
|
||||
Age int
|
||||
EducationLevel string
|
||||
Country string
|
||||
Region string
|
||||
|
||||
// Profile fields
|
||||
NickName string
|
||||
Occupation string
|
||||
LearningGoal string
|
||||
LanguageGoal string
|
||||
LanguageChallange string
|
||||
FavoutiteTopic string
|
||||
|
||||
PreferredLanguage string
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +170,6 @@ type ResetPasswordReq struct {
|
|||
Password string
|
||||
OtpCode string
|
||||
}
|
||||
|
||||
type UpdateUserReq struct {
|
||||
UserID int64
|
||||
|
||||
|
|
@ -142,6 +184,14 @@ type UpdateUserReq struct {
|
|||
Country ValidString
|
||||
Region ValidString
|
||||
|
||||
// Profile fields
|
||||
NickName ValidString
|
||||
Occupation ValidString
|
||||
LearningGoal ValidString
|
||||
LanguageGoal ValidString
|
||||
LanguageChallange ValidString
|
||||
FavoutiteTopic ValidString
|
||||
|
||||
ProfileCompleted ValidBool
|
||||
ProfilePictureURL ValidString
|
||||
PreferredLanguage ValidString
|
||||
|
|
|
|||
16
internal/ports/initial_assessment.go
Normal file
16
internal/ports/initial_assessment.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package ports
|
||||
|
||||
import (
|
||||
"Yimaru-Backend/internal/domain"
|
||||
"context"
|
||||
)
|
||||
|
||||
type InitialAssessmentStore interface {
|
||||
CreateAssessmentQuestion(
|
||||
ctx context.Context,
|
||||
q domain.AssessmentQuestion,
|
||||
) (domain.AssessmentQuestion, error)
|
||||
GetActiveAssessmentQuestions(ctx context.Context) ([]domain.AssessmentQuestion, error)
|
||||
SaveAssessmentAttempt(ctx context.Context, userID int64, answers []domain.UserAnswer) (domain.AssessmentAttempt, error)
|
||||
GetOptionByID(ctx context.Context, optionID int64) (domain.AssessmentOption, error)
|
||||
}
|
||||
|
|
@ -4,10 +4,20 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
dbgen "Yimaru-Backend/gen/db"
|
||||
"Yimaru-Backend/internal/domain"
|
||||
)
|
||||
|
||||
type UserStore interface {
|
||||
GetCorrectOptionForQuestion(
|
||||
ctx context.Context,
|
||||
questionID int64,
|
||||
) (int64, error)
|
||||
GetLatestAssessmentAttempt(
|
||||
ctx context.Context,
|
||||
userID int64,
|
||||
) (*dbgen.AssessmentAttempt, error)
|
||||
UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error
|
||||
IsUserNameUnique(ctx context.Context, userName string) (bool, error)
|
||||
IsUserPending(ctx context.Context, UserName string) (bool, error)
|
||||
GetUserByUserName(
|
||||
|
|
@ -48,24 +58,7 @@ type UserStore interface {
|
|||
email string,
|
||||
phone string,
|
||||
) (domain.User, error)
|
||||
UpdatePassword(ctx context.Context, password, email, phone string, updatedAt time.Time) error
|
||||
// GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error)
|
||||
// GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error)
|
||||
// UpdateUserSuspend(ctx context.Context, id int64, status bool) error
|
||||
|
||||
// UpdateUser(ctx context.Context, user domain.UpdateUserReq) error
|
||||
// UpdateUserSuspend(ctx context.Context, id int64, status bool) error
|
||||
// DeleteUser(ctx context.Context, id int64) error
|
||||
// CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error)
|
||||
// GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error)
|
||||
// GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error)
|
||||
// SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error)
|
||||
// UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error
|
||||
// GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error)
|
||||
|
||||
// GetCustomerCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
||||
// GetCustomerDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerDetail, error)
|
||||
// GetRoleCounts(ctx context.Context, role string, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
||||
UpdatePassword(ctx context.Context, password, userName string) error
|
||||
}
|
||||
type SmsGateway interface {
|
||||
SendSMSOTP(ctx context.Context, phoneNumber, otp string) error
|
||||
|
|
|
|||
175
internal/repository/initial_assessment.go
Normal file
175
internal/repository/initial_assessment.go
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
dbgen "Yimaru-Backend/gen/db"
|
||||
"Yimaru-Backend/internal/domain"
|
||||
"Yimaru-Backend/internal/ports"
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
func NewInitialAssessmentStore(s *Store) ports.InitialAssessmentStore { return s }
|
||||
|
||||
func (r *Store) GetCorrectOptionForQuestion(
|
||||
ctx context.Context,
|
||||
questionID int64,
|
||||
) (int64, error) {
|
||||
|
||||
optId, err := r.queries.GetCorrectOptionForQuestion(ctx, questionID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return optId, nil
|
||||
}
|
||||
|
||||
func (r *Store) GetLatestAssessmentAttempt(
|
||||
ctx context.Context,
|
||||
userID int64,
|
||||
) (*dbgen.AssessmentAttempt, error) {
|
||||
|
||||
attempt, err := r.queries.GetLatestAssessmentAttempt(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &attempt, nil
|
||||
}
|
||||
|
||||
func (s *Store) CreateAssessmentQuestion(
|
||||
ctx context.Context,
|
||||
q domain.AssessmentQuestion,
|
||||
) (domain.AssessmentQuestion, error) {
|
||||
|
||||
row, err := s.queries.CreateAssessmentQuestion(ctx, dbgen.CreateAssessmentQuestionParams{
|
||||
Title: q.Title,
|
||||
Description: pgtype.Text{String: q.Description, Valid: q.Description != ""},
|
||||
QuestionType: q.QuestionType,
|
||||
DifficultyLevel: q.DifficultyLevel,
|
||||
})
|
||||
if err != nil {
|
||||
return domain.AssessmentQuestion{}, err
|
||||
}
|
||||
|
||||
for _, opt := range q.Options {
|
||||
if err := s.queries.CreateAssessmentQuestionOption(ctx,
|
||||
dbgen.CreateAssessmentQuestionOptionParams{
|
||||
QuestionID: row.ID,
|
||||
OptionText: opt.OptionText,
|
||||
IsCorrect: opt.IsCorrect,
|
||||
},
|
||||
); err != nil {
|
||||
return domain.AssessmentQuestion{}, err
|
||||
}
|
||||
}
|
||||
|
||||
q.ID = row.ID
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetActiveAssessmentQuestions(ctx context.Context) ([]domain.AssessmentQuestion, error) {
|
||||
questionsRows, err := s.queries.GetActiveAssessmentQuestions(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
questions := make([]domain.AssessmentQuestion, 0, len(questionsRows))
|
||||
for _, q := range questionsRows {
|
||||
optionsRows, err := s.queries.GetQuestionOptions(ctx, q.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options := make([]domain.AssessmentOption, 0, len(optionsRows))
|
||||
for _, o := range optionsRows {
|
||||
options = append(options, domain.AssessmentOption{
|
||||
ID: o.ID,
|
||||
OptionText: o.OptionText,
|
||||
IsCorrect: o.IsCorrect,
|
||||
})
|
||||
}
|
||||
|
||||
questions = append(questions, domain.AssessmentQuestion{
|
||||
ID: q.ID,
|
||||
Title: q.Title,
|
||||
Description: q.Description.String,
|
||||
QuestionType: q.QuestionType,
|
||||
DifficultyLevel: q.DifficultyLevel,
|
||||
Options: options,
|
||||
})
|
||||
}
|
||||
|
||||
return questions, nil
|
||||
}
|
||||
|
||||
// SaveAssessmentAttempt saves the attempt summary and answers
|
||||
func (s *Store) SaveAssessmentAttempt(ctx context.Context, userID int64, answers []domain.UserAnswer) (domain.AssessmentAttempt, error) {
|
||||
total := len(answers)
|
||||
correct := 0
|
||||
|
||||
for _, ans := range answers {
|
||||
if ans.IsCorrect {
|
||||
correct++
|
||||
}
|
||||
}
|
||||
|
||||
score := float64(correct) / float64(total) * 100
|
||||
knowledgeLevel := "BEGINNER"
|
||||
switch {
|
||||
case score >= 80:
|
||||
knowledgeLevel = "ADVANCED"
|
||||
case score >= 50:
|
||||
knowledgeLevel = "INTERMEDIATE"
|
||||
}
|
||||
|
||||
// Save attempt
|
||||
attemptRow, err := s.queries.CreateAssessmentAttempt(ctx, dbgen.CreateAssessmentAttemptParams{
|
||||
UserID: userID,
|
||||
TotalQuestions: int32(total),
|
||||
CorrectAnswers: int32(correct),
|
||||
ScorePercentage: pgtype.Numeric{Int: big.NewInt(int64(score * 100)), Valid: true},
|
||||
KnowledgeLevel: knowledgeLevel,
|
||||
})
|
||||
if err != nil {
|
||||
return domain.AssessmentAttempt{}, err
|
||||
}
|
||||
|
||||
// Save answers
|
||||
for _, ans := range answers {
|
||||
err := s.queries.CreateAssessmentAnswer(ctx, dbgen.CreateAssessmentAnswerParams{
|
||||
AttemptID: attemptRow.ID,
|
||||
QuestionID: ans.QuestionID,
|
||||
SelectedOptionID: pgtype.Int8{Int64: ans.SelectedOptionID, Valid: true},
|
||||
ShortAnswer: pgtype.Text{String: ans.ShortAnswer, Valid: true},
|
||||
IsCorrect: ans.IsCorrect,
|
||||
})
|
||||
if err != nil {
|
||||
return domain.AssessmentAttempt{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return domain.AssessmentAttempt{
|
||||
ID: attemptRow.ID,
|
||||
UserID: userID,
|
||||
TotalQuestions: total,
|
||||
CorrectAnswers: correct,
|
||||
ScorePercentage: score,
|
||||
KnowledgeLevel: knowledgeLevel,
|
||||
CompletedAt: attemptRow.CompletedAt.Time,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetOptionByID fetches a single option to validate correctness
|
||||
func (s *Store) GetOptionByID(ctx context.Context, optionID int64) (domain.AssessmentOption, error) {
|
||||
o, err := s.queries.GetAssessmentOptionByID(ctx, optionID)
|
||||
if err != nil {
|
||||
return domain.AssessmentOption{}, err
|
||||
}
|
||||
return domain.AssessmentOption{
|
||||
ID: o.ID,
|
||||
OptionText: o.OptionText,
|
||||
IsCorrect: o.IsCorrect,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -16,6 +16,13 @@ import (
|
|||
|
||||
func NewUserStore(s *Store) ports.UserStore { return s }
|
||||
|
||||
func (s *Store) UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error {
|
||||
return s.queries.UpdateUserKnowledgeLevel(ctx, dbgen.UpdateUserKnowledgeLevelParams{
|
||||
ID: userID,
|
||||
KnowledgeLevel: pgtype.Text{String: knowledgeLevel, Valid: true},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) IsUserPending(ctx context.Context, UserName string) (bool, error) {
|
||||
isPending, err := s.queries.IsUserPending(ctx, UserName)
|
||||
if err != nil {
|
||||
|
|
@ -56,17 +63,44 @@ func (s *Store) CreateUserWithoutOtp(
|
|||
Country: pgtype.Text{String: user.Country, Valid: user.Country != ""},
|
||||
Region: pgtype.Text{String: user.Region, Valid: user.Region != ""},
|
||||
|
||||
NickName: pgtype.Text{
|
||||
String: user.NickName,
|
||||
Valid: user.NickName != "",
|
||||
},
|
||||
Occupation: pgtype.Text{
|
||||
String: user.Occupation,
|
||||
Valid: user.Occupation != "",
|
||||
},
|
||||
LearningGoal: pgtype.Text{
|
||||
String: user.LearningGoal,
|
||||
Valid: user.LearningGoal != "",
|
||||
},
|
||||
LanguageGoal: pgtype.Text{
|
||||
String: user.LanguageGoal,
|
||||
Valid: user.LanguageGoal != "",
|
||||
},
|
||||
LanguageChallange: pgtype.Text{
|
||||
String: user.LanguageChallange,
|
||||
Valid: user.LanguageChallange != "",
|
||||
},
|
||||
FavoutiteTopic: pgtype.Text{
|
||||
String: user.FavoutiteTopic,
|
||||
Valid: user.FavoutiteTopic != "",
|
||||
},
|
||||
|
||||
EmailVerified: user.EmailVerified,
|
||||
PhoneVerified: user.PhoneVerified,
|
||||
|
||||
ProfilePictureUrl: pgtype.Text{
|
||||
String: user.ProfilePictureURL,
|
||||
Valid: user.ProfilePictureURL != "",
|
||||
},
|
||||
Status: string(user.Status),
|
||||
ProfileCompleted: user.ProfileCompleted,
|
||||
PreferredLanguage: pgtype.Text{
|
||||
String: user.PreferredLanguage,
|
||||
Valid: user.PreferredLanguage != "",
|
||||
},
|
||||
|
||||
// OrganizationID: user.OrganizationID.ToPG(),
|
||||
})
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
|
|
@ -77,31 +111,7 @@ func (s *Store) CreateUserWithoutOtp(
|
|||
updatedAt = &userRes.UpdatedAt.Time
|
||||
}
|
||||
|
||||
return domain.User{
|
||||
ID: userRes.ID,
|
||||
FirstName: userRes.FirstName,
|
||||
LastName: userRes.LastName,
|
||||
UserName: userRes.UserName,
|
||||
Email: userRes.Email.String,
|
||||
PhoneNumber: userRes.PhoneNumber.String,
|
||||
Role: domain.Role(userRes.Role),
|
||||
Password: user.Password,
|
||||
|
||||
Age: int(userRes.Age.Int32),
|
||||
EducationLevel: userRes.EducationLevel.String,
|
||||
Country: userRes.Country.String,
|
||||
Region: userRes.Region.String,
|
||||
|
||||
EmailVerified: userRes.EmailVerified,
|
||||
PhoneVerified: userRes.PhoneVerified,
|
||||
Status: domain.UserStatus(userRes.Status),
|
||||
|
||||
ProfileCompleted: userRes.ProfileCompleted,
|
||||
PreferredLanguage: userRes.PreferredLanguage.String,
|
||||
|
||||
CreatedAt: userRes.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
}, nil
|
||||
return mapCreateUserResult(userRes, user.Password, updatedAt), nil
|
||||
}
|
||||
|
||||
// CreateUser inserts a new user into the database
|
||||
|
|
@ -111,7 +121,6 @@ func (s *Store) CreateUser(
|
|||
usedOtpId int64,
|
||||
) (domain.User, error) {
|
||||
|
||||
// Optional: mark OTP as used
|
||||
if usedOtpId > 0 {
|
||||
if err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{
|
||||
ID: usedOtpId,
|
||||
|
|
@ -137,17 +146,26 @@ func (s *Store) CreateUser(
|
|||
Country: pgtype.Text{String: user.Country, Valid: user.Country != ""},
|
||||
Region: pgtype.Text{String: user.Region, Valid: user.Region != ""},
|
||||
|
||||
NickName: pgtype.Text{String: user.NickName, Valid: user.NickName != ""},
|
||||
Occupation: pgtype.Text{String: user.Occupation, Valid: user.Occupation != ""},
|
||||
LearningGoal: pgtype.Text{String: user.LearningGoal, Valid: user.LearningGoal != ""},
|
||||
LanguageGoal: pgtype.Text{String: user.LanguageGoal, Valid: user.LanguageGoal != ""},
|
||||
LanguageChallange: pgtype.Text{String: user.LanguageChallange, Valid: user.LanguageChallange != ""},
|
||||
FavoutiteTopic: pgtype.Text{String: user.FavoutiteTopic, Valid: user.FavoutiteTopic != ""},
|
||||
|
||||
EmailVerified: user.EmailVerified,
|
||||
PhoneVerified: user.PhoneVerified,
|
||||
|
||||
ProfilePictureUrl: pgtype.Text{
|
||||
String: user.ProfilePictureURL,
|
||||
Valid: user.ProfilePictureURL != "",
|
||||
},
|
||||
Status: string(user.Status),
|
||||
ProfileCompleted: user.ProfileCompleted,
|
||||
PreferredLanguage: pgtype.Text{
|
||||
String: user.PreferredLanguage,
|
||||
Valid: user.PreferredLanguage != "",
|
||||
},
|
||||
|
||||
// OrganizationID: user.OrganizationID.ToPG(),
|
||||
})
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
|
|
@ -158,31 +176,7 @@ func (s *Store) CreateUser(
|
|||
updatedAt = &userRes.UpdatedAt.Time
|
||||
}
|
||||
|
||||
return domain.User{
|
||||
ID: userRes.ID,
|
||||
FirstName: userRes.FirstName,
|
||||
LastName: userRes.LastName,
|
||||
UserName: userRes.UserName,
|
||||
Email: userRes.Email.String,
|
||||
PhoneNumber: userRes.PhoneNumber.String,
|
||||
Role: domain.Role(userRes.Role),
|
||||
Password: user.Password,
|
||||
|
||||
Age: int(userRes.Age.Int32),
|
||||
EducationLevel: userRes.EducationLevel.String,
|
||||
Country: userRes.Country.String,
|
||||
Region: userRes.Region.String,
|
||||
|
||||
EmailVerified: userRes.EmailVerified,
|
||||
PhoneVerified: userRes.PhoneVerified,
|
||||
Status: domain.UserStatus(userRes.Status),
|
||||
|
||||
ProfileCompleted: userRes.ProfileCompleted,
|
||||
PreferredLanguage: userRes.PreferredLanguage.String,
|
||||
|
||||
CreatedAt: userRes.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
}, nil
|
||||
return mapCreateUserResult(userRes, user.Password, updatedAt), nil
|
||||
}
|
||||
|
||||
// GetUserByID retrieves a user by ID
|
||||
|
|
@ -223,6 +217,13 @@ func (s *Store) GetUserByID(
|
|||
Country: u.Country.String,
|
||||
Region: u.Region.String,
|
||||
|
||||
NickName: u.NickName.String,
|
||||
Occupation: u.Occupation.String,
|
||||
LearningGoal: u.LearningGoal.String,
|
||||
LanguageGoal: u.LanguageGoal.String,
|
||||
LanguageChallange: u.LanguageChallange.String,
|
||||
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||
|
||||
EmailVerified: u.EmailVerified,
|
||||
PhoneVerified: u.PhoneVerified,
|
||||
Status: domain.UserStatus(u.Status),
|
||||
|
|
@ -232,10 +233,6 @@ func (s *Store) GetUserByID(
|
|||
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||
PreferredLanguage: u.PreferredLanguage.String,
|
||||
|
||||
// OrganizationID: domain.ValidInt64{
|
||||
// Value: u.OrganizationID.Int64,
|
||||
// Valid: u.OrganizationID.Valid,
|
||||
// },
|
||||
CreatedAt: u.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
}, nil
|
||||
|
|
@ -259,10 +256,6 @@ func (s *Store) GetAllUsers(
|
|||
params.Role = *role
|
||||
}
|
||||
|
||||
// if organizationID != nil {
|
||||
// params.OrganizationID = pgtype.Int8{Int64: *organizationID, Valid: true}
|
||||
// }
|
||||
|
||||
if query != nil {
|
||||
params.Query = pgtype.Text{String: *query, Valid: true}
|
||||
}
|
||||
|
|
@ -308,17 +301,21 @@ func (s *Store) GetAllUsers(
|
|||
Country: u.Country.String,
|
||||
Region: u.Region.String,
|
||||
|
||||
NickName: u.NickName.String,
|
||||
Occupation: u.Occupation.String,
|
||||
LearningGoal: u.LearningGoal.String,
|
||||
LanguageGoal: u.LanguageGoal.String,
|
||||
LanguageChallange: u.LanguageChallange.String,
|
||||
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||
|
||||
EmailVerified: u.EmailVerified,
|
||||
PhoneVerified: u.PhoneVerified,
|
||||
Status: domain.UserStatus(u.Status),
|
||||
|
||||
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||
ProfileCompleted: u.ProfileCompleted,
|
||||
PreferredLanguage: u.PreferredLanguage.String,
|
||||
|
||||
// OrganizationID: domain.ValidInt64{
|
||||
// Value: u.OrganizationID.Int64,
|
||||
// Valid: u.OrganizationID.Valid,
|
||||
// },
|
||||
CreatedAt: u.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
|
|
@ -350,13 +347,6 @@ func (s *Store) SearchUserByNameOrPhone(
|
|||
},
|
||||
}
|
||||
|
||||
// if organizationID != nil {
|
||||
// params.OrganizationID = pgtype.Int8{
|
||||
// Int64: *organizationID,
|
||||
// Valid: true,
|
||||
// }
|
||||
// }
|
||||
|
||||
if role != nil {
|
||||
params.Role = pgtype.Text{
|
||||
String: *role,
|
||||
|
|
@ -369,7 +359,12 @@ func (s *Store) SearchUserByNameOrPhone(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
return []domain.User{}, nil
|
||||
}
|
||||
|
||||
users := make([]domain.User, 0, len(rows))
|
||||
|
||||
for _, u := range rows {
|
||||
|
||||
var updatedAt *time.Time
|
||||
|
|
@ -391,16 +386,21 @@ func (s *Store) SearchUserByNameOrPhone(
|
|||
Country: u.Country.String,
|
||||
Region: u.Region.String,
|
||||
|
||||
NickName: u.NickName.String,
|
||||
Occupation: u.Occupation.String,
|
||||
LearningGoal: u.LearningGoal.String,
|
||||
LanguageGoal: u.LanguageGoal.String,
|
||||
LanguageChallange: u.LanguageChallange.String,
|
||||
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||
|
||||
EmailVerified: u.EmailVerified,
|
||||
PhoneVerified: u.PhoneVerified,
|
||||
Status: domain.UserStatus(u.Status),
|
||||
|
||||
ProfileCompleted: u.ProfileCompleted,
|
||||
ProfileCompleted: u.ProfileCompleted,
|
||||
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||
PreferredLanguage: u.PreferredLanguage.String,
|
||||
|
||||
// OrganizationID: domain.ValidInt64{
|
||||
// Value: u.OrganizationID.Int64,
|
||||
// Valid: u.OrganizationID.Valid,
|
||||
// },
|
||||
CreatedAt: u.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
|
|
@ -410,32 +410,73 @@ func (s *Store) SearchUserByNameOrPhone(
|
|||
}
|
||||
|
||||
// UpdateUser updates basic user info
|
||||
func (s *Store) UpdateUser(ctx context.Context, user domain.User) error {
|
||||
func (s *Store) UpdateUser(
|
||||
ctx context.Context,
|
||||
user domain.User,
|
||||
) error {
|
||||
|
||||
return s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{
|
||||
ID: user.ID,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Status: string(user.Status),
|
||||
UserName: user.UserName,
|
||||
|
||||
Age: pgtype.Int4{
|
||||
Int32: int32(user.Age),
|
||||
Valid: user.Age > 0,
|
||||
},
|
||||
EducationLevel: pgtype.Text{
|
||||
String: user.EducationLevel,
|
||||
Valid: user.EducationLevel != "",
|
||||
},
|
||||
Country: pgtype.Text{
|
||||
String: user.Country,
|
||||
Valid: user.Country != "",
|
||||
},
|
||||
Region: pgtype.Text{
|
||||
String: user.Region,
|
||||
Valid: user.Region != "",
|
||||
},
|
||||
|
||||
NickName: pgtype.Text{
|
||||
String: user.NickName,
|
||||
Valid: user.NickName != "",
|
||||
},
|
||||
Occupation: pgtype.Text{
|
||||
String: user.Occupation,
|
||||
Valid: user.Occupation != "",
|
||||
},
|
||||
LearningGoal: pgtype.Text{
|
||||
String: user.LearningGoal,
|
||||
Valid: user.LearningGoal != "",
|
||||
},
|
||||
LanguageGoal: pgtype.Text{
|
||||
String: user.LanguageGoal,
|
||||
Valid: user.LanguageGoal != "",
|
||||
},
|
||||
LanguageChallange: pgtype.Text{
|
||||
String: user.LanguageChallange,
|
||||
Valid: user.LanguageChallange != "",
|
||||
},
|
||||
FavoutiteTopic: pgtype.Text{
|
||||
String: user.FavoutiteTopic,
|
||||
Valid: user.FavoutiteTopic != "",
|
||||
},
|
||||
|
||||
Status: string(user.Status),
|
||||
ProfileCompleted: user.ProfileCompleted,
|
||||
ProfilePictureUrl: pgtype.Text{
|
||||
String: user.ProfilePictureURL,
|
||||
Valid: user.ProfilePictureURL != "",
|
||||
},
|
||||
PreferredLanguage: pgtype.Text{
|
||||
String: user.PreferredLanguage,
|
||||
Valid: user.PreferredLanguage != "",
|
||||
},
|
||||
|
||||
ID: user.ID,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUserOrganization updates a user's organization
|
||||
// func (s *Store) UpdateUserOrganization(ctx context.Context, userID, organizationID int64) error {
|
||||
// return s.queries.UpdateUserOrganization(ctx, dbgen.UpdateUserOrganizationParams{
|
||||
// OrganizationID: pgtype.Int8{Int64: organizationID, Valid: true},
|
||||
// ID: userID,
|
||||
// })
|
||||
// }
|
||||
|
||||
// SuspendUser suspends a user
|
||||
// func (s *Store) SuspendUser(ctx context.Context, userID int64, suspended bool, suspendedAt time.Time) error {
|
||||
// return s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{
|
||||
// Suspended: suspended,
|
||||
// SuspendedAt: pgtype.Timestamptz{Time: suspendedAt, Valid: true},
|
||||
// ID: userID,
|
||||
// })
|
||||
// }
|
||||
|
||||
// DeleteUser removes a user
|
||||
func (s *Store) DeleteUser(ctx context.Context, userID int64) error {
|
||||
return s.queries.DeleteUser(ctx, userID)
|
||||
|
|
@ -461,7 +502,7 @@ func (s *Store) GetUserByUserName(
|
|||
|
||||
u, err := s.queries.GetUserByUserName(ctx, userName)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return domain.User{}, authentication.ErrUserNotFound
|
||||
}
|
||||
return domain.User{}, err
|
||||
|
|
@ -492,18 +533,22 @@ func (s *Store) GetUserByUserName(
|
|||
Country: u.Country.String,
|
||||
Region: u.Region.String,
|
||||
|
||||
NickName: u.NickName.String,
|
||||
Occupation: u.Occupation.String,
|
||||
LearningGoal: u.LearningGoal.String,
|
||||
LanguageGoal: u.LanguageGoal.String,
|
||||
LanguageChallange: u.LanguageChallange.String,
|
||||
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||
|
||||
EmailVerified: u.EmailVerified,
|
||||
PhoneVerified: u.PhoneVerified,
|
||||
Status: domain.UserStatus(u.Status),
|
||||
|
||||
LastLogin: lastLogin,
|
||||
ProfileCompleted: u.ProfileCompleted,
|
||||
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||
PreferredLanguage: u.PreferredLanguage.String,
|
||||
|
||||
// OrganizationID: domain.ValidInt64{
|
||||
// Value: u.OrganizationID.Int64,
|
||||
// Valid: u.OrganizationID.Valid,
|
||||
// },
|
||||
CreatedAt: u.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
}, nil
|
||||
|
|
@ -558,88 +603,72 @@ func (s *Store) GetUserByEmailPhone(
|
|||
Country: u.Country.String,
|
||||
Region: u.Region.String,
|
||||
|
||||
NickName: u.NickName.String,
|
||||
Occupation: u.Occupation.String,
|
||||
LearningGoal: u.LearningGoal.String,
|
||||
LanguageGoal: u.LanguageGoal.String,
|
||||
LanguageChallange: u.LanguageChallange.String,
|
||||
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||
|
||||
EmailVerified: u.EmailVerified,
|
||||
PhoneVerified: u.PhoneVerified,
|
||||
Status: domain.UserStatus(u.Status),
|
||||
|
||||
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||
LastLogin: lastLogin,
|
||||
ProfileCompleted: u.ProfileCompleted,
|
||||
PreferredLanguage: u.PreferredLanguage.String,
|
||||
|
||||
// OrganizationID: domain.ValidInt64{
|
||||
// Value: u.OrganizationID.Int64,
|
||||
// Valid: u.OrganizationID.Valid,
|
||||
// },
|
||||
CreatedAt: u.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdatePassword updates a user's password
|
||||
func (s *Store) UpdatePassword(ctx context.Context, password, email, phone string, updatedAt time.Time) error {
|
||||
func (s *Store) UpdatePassword(ctx context.Context, password, userName string) error {
|
||||
return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
||||
Password: []byte(password),
|
||||
Email: pgtype.Text{String: email},
|
||||
PhoneNumber: pgtype.Text{String: phone},
|
||||
// OrganizationID: pgtype.Int8{Int64: organizationID},
|
||||
Password: []byte(password),
|
||||
UserName: userName,
|
||||
})
|
||||
}
|
||||
|
||||
// GetOwnerByOrganizationID retrieves the owner user of an organization
|
||||
// func (s *Store) GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error) {
|
||||
// userRes, err := s.queries.GetOwnerByOrganizationID(ctx, organizationID)
|
||||
// if err != nil {
|
||||
// return domain.User{}, err
|
||||
// }
|
||||
// return mapUser(userRes), nil
|
||||
// }
|
||||
|
||||
// func (s *Store) UpdateUserSuspend(ctx context.Context, id int64, status bool) error {
|
||||
// err := s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{
|
||||
// ID: id,
|
||||
// Suspended: status,
|
||||
// SuspendedAt: pgtype.Timestamptz{
|
||||
// Time: time.Now(),
|
||||
// Valid: true,
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// mapUser converts dbgen.User to domain.User
|
||||
func MapUser(u dbgen.User) domain.User {
|
||||
func mapCreateUserResult(
|
||||
userRes dbgen.CreateUserRow,
|
||||
password []byte,
|
||||
updatedAt *time.Time,
|
||||
) domain.User {
|
||||
|
||||
return domain.User{
|
||||
ID: u.ID,
|
||||
FirstName: u.FirstName,
|
||||
LastName: u.LastName,
|
||||
ID: userRes.ID,
|
||||
FirstName: userRes.FirstName,
|
||||
LastName: userRes.LastName,
|
||||
UserName: userRes.UserName,
|
||||
Email: userRes.Email.String,
|
||||
PhoneNumber: userRes.PhoneNumber.String,
|
||||
Role: domain.Role(userRes.Role),
|
||||
Password: password,
|
||||
|
||||
UserName: u.UserName,
|
||||
Email: u.Email.String,
|
||||
PhoneNumber: u.PhoneNumber.String,
|
||||
Age: int(userRes.Age.Int32),
|
||||
EducationLevel: userRes.EducationLevel.String,
|
||||
Country: userRes.Country.String,
|
||||
Region: userRes.Region.String,
|
||||
|
||||
Role: domain.Role(u.Role),
|
||||
NickName: userRes.NickName.String,
|
||||
Occupation: userRes.Occupation.String,
|
||||
LearningGoal: userRes.LearningGoal.String,
|
||||
LanguageGoal: userRes.LanguageGoal.String,
|
||||
LanguageChallange: userRes.LanguageChallange.String,
|
||||
FavoutiteTopic: userRes.FavoutiteTopic.String,
|
||||
|
||||
Age: int(u.Age.Int32),
|
||||
EducationLevel: u.EducationLevel.String,
|
||||
Country: u.Country.String,
|
||||
Region: u.Region.String,
|
||||
EmailVerified: userRes.EmailVerified,
|
||||
PhoneVerified: userRes.PhoneVerified,
|
||||
Status: domain.UserStatus(userRes.Status),
|
||||
|
||||
EmailVerified: u.EmailVerified,
|
||||
PhoneVerified: u.PhoneVerified,
|
||||
Status: domain.UserStatus(u.Status),
|
||||
LastLogin: &u.LastLogin.Time,
|
||||
ProfileCompleted: u.ProfileCompleted,
|
||||
PreferredLanguage: u.PreferredLanguage.String,
|
||||
ProfileCompleted: userRes.ProfileCompleted,
|
||||
PreferredLanguage: userRes.PreferredLanguage.String,
|
||||
|
||||
// OrganizationID: domain.ValidInt64{
|
||||
// Value: u.OrganizationID.Int64,
|
||||
// Valid: u.OrganizationID.Valid,
|
||||
// },
|
||||
|
||||
CreatedAt: u.CreatedAt.Time,
|
||||
CreatedAt: userRes.CreatedAt.Time,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
181
internal/services/assessment/initial_assessment.go
Normal file
181
internal/services/assessment/initial_assessment.go
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
package assessment
|
||||
|
||||
import (
|
||||
"Yimaru-Backend/internal/domain"
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s *Service) GetActiveAssessmentQuestions(
|
||||
ctx context.Context,
|
||||
) ([]domain.AssessmentQuestion, error) {
|
||||
|
||||
questions, err := s.initialAssessmentStore.GetActiveAssessmentQuestions(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// IMPORTANT:
|
||||
// Do NOT expose correct answers to the client
|
||||
for i := range questions {
|
||||
for j := range questions[i].Options {
|
||||
questions[i].Options[j].IsCorrect = false
|
||||
}
|
||||
}
|
||||
|
||||
return questions, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateAssessmentQuestion(
|
||||
ctx context.Context,
|
||||
q domain.AssessmentQuestion,
|
||||
) (domain.AssessmentQuestion, error) {
|
||||
|
||||
// Basic validation
|
||||
if q.Title == "" {
|
||||
return domain.AssessmentQuestion{}, errors.New("question title is required")
|
||||
}
|
||||
|
||||
if q.QuestionType == "" {
|
||||
return domain.AssessmentQuestion{}, errors.New("question type is required")
|
||||
}
|
||||
|
||||
if q.DifficultyLevel == "" {
|
||||
return domain.AssessmentQuestion{}, errors.New("difficulty level is required")
|
||||
}
|
||||
|
||||
// Multiple choice / true-false must have options
|
||||
if q.QuestionType != string(domain.QuestionTypeShortAnswer) {
|
||||
if len(q.Options) < 2 {
|
||||
return domain.AssessmentQuestion{}, errors.New("at least two options are required")
|
||||
}
|
||||
|
||||
hasCorrect := false
|
||||
for _, opt := range q.Options {
|
||||
if opt.OptionText == "" {
|
||||
return domain.AssessmentQuestion{}, errors.New("option text cannot be empty")
|
||||
}
|
||||
if opt.IsCorrect {
|
||||
hasCorrect = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasCorrect {
|
||||
return domain.AssessmentQuestion{}, errors.New("at least one correct option is required")
|
||||
}
|
||||
}
|
||||
|
||||
// Persist via repository
|
||||
return s.initialAssessmentStore.CreateAssessmentQuestion(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) SubmitAssessment(
|
||||
ctx context.Context,
|
||||
userID int64,
|
||||
responses []domain.UserAnswer,
|
||||
) (domain.AssessmentAttempt, error) {
|
||||
|
||||
if userID <= 0 {
|
||||
return domain.AssessmentAttempt{}, errors.New("invalid user id")
|
||||
}
|
||||
|
||||
if len(responses) == 0 {
|
||||
return domain.AssessmentAttempt{}, errors.New("no responses submitted")
|
||||
}
|
||||
|
||||
// Step 1: Validate and evaluate answers
|
||||
for i, ans := range responses {
|
||||
if ans.QuestionID == 0 {
|
||||
return domain.AssessmentAttempt{}, errors.New("invalid question id")
|
||||
}
|
||||
|
||||
isCorrect, err := s.validateAnswer(ctx, ans)
|
||||
if err != nil {
|
||||
return domain.AssessmentAttempt{}, err
|
||||
}
|
||||
|
||||
responses[i].IsCorrect = isCorrect
|
||||
}
|
||||
|
||||
// Step 2: Persist assessment attempt + answers
|
||||
attempt, err := s.initialAssessmentStore.SaveAssessmentAttempt(
|
||||
ctx,
|
||||
userID,
|
||||
responses,
|
||||
)
|
||||
if err != nil {
|
||||
return domain.AssessmentAttempt{}, err
|
||||
}
|
||||
|
||||
// Step 3: Update user's knowledge level
|
||||
if err := s.userStore.UpdateUserKnowledgeLevel(
|
||||
ctx,
|
||||
userID,
|
||||
attempt.KnowledgeLevel,
|
||||
); err != nil {
|
||||
return domain.AssessmentAttempt{}, err
|
||||
}
|
||||
|
||||
// Step 4: Send in-app notification
|
||||
notification := &domain.Notification{
|
||||
|
||||
RecipientID: userID,
|
||||
Level: domain.NotificationLevelInfo,
|
||||
Reciever: domain.NotificationRecieverSideCustomer,
|
||||
IsRead: false,
|
||||
DeliveryStatus: domain.DeliveryStatusSent,
|
||||
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "Knowledge Assessment Completed",
|
||||
Message: "Your knowledge assessment is complete. Your knowledge level is " + attempt.KnowledgeLevel + ".",
|
||||
Tags: []string{"assessment", "knowledge-level"},
|
||||
},
|
||||
Timestamp: time.Now(),
|
||||
Type: domain.NOTIFICATION_TYPE_KNOWLEDGE_LEVEL_UPDATE,
|
||||
}
|
||||
|
||||
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
||||
return domain.AssessmentAttempt{}, err
|
||||
}
|
||||
|
||||
return attempt, nil
|
||||
}
|
||||
|
||||
func (s *Service) validateAnswer(
|
||||
ctx context.Context,
|
||||
answer domain.UserAnswer,
|
||||
) (bool, error) {
|
||||
|
||||
// Multiple choice / True-False
|
||||
if answer.SelectedOptionID != 0 {
|
||||
option, err := s.initialAssessmentStore.GetOptionByID(
|
||||
ctx,
|
||||
answer.SelectedOptionID,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return option.IsCorrect, nil
|
||||
}
|
||||
|
||||
// Short answer (future-proofing)
|
||||
if answer.ShortAnswer != "" {
|
||||
// Placeholder: subjective/manual evaluation
|
||||
// For now, mark incorrect
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, errors.New("invalid answer submission")
|
||||
}
|
||||
|
||||
func CalculateKnowledgeLevel(score float64) string {
|
||||
switch {
|
||||
case score >= 80:
|
||||
return "ADVANCED"
|
||||
case score >= 50:
|
||||
return "INTERMEDIATE"
|
||||
default:
|
||||
return "BEGINNER"
|
||||
}
|
||||
}
|
||||
31
internal/services/assessment/service.go
Normal file
31
internal/services/assessment/service.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package assessment
|
||||
|
||||
import (
|
||||
"Yimaru-Backend/internal/config"
|
||||
"Yimaru-Backend/internal/ports"
|
||||
notificationservice "Yimaru-Backend/internal/services/notification"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
userStore ports.UserStore
|
||||
initialAssessmentStore ports.InitialAssessmentStore
|
||||
notificationSvc *notificationservice.Service
|
||||
// messengerSvc *messenger.Service
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
func NewService(
|
||||
userStore ports.UserStore,
|
||||
initialAssessmentStore ports.InitialAssessmentStore,
|
||||
notificationSvc *notificationservice.Service,
|
||||
// messengerSvc *messenger.Service,
|
||||
cfg *config.Config,
|
||||
) *Service {
|
||||
return &Service{
|
||||
userStore: userStore,
|
||||
initialAssessmentStore: initialAssessmentStore,
|
||||
notificationSvc: notificationSvc,
|
||||
// messengerSvc: messengerSvc,
|
||||
config: cfg,
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,10 @@ import (
|
|||
"context"
|
||||
)
|
||||
|
||||
func (s *Service) UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error {
|
||||
return s.userStore.UpdateUserKnowledgeLevel(ctx, userID, knowledgeLevel)
|
||||
}
|
||||
|
||||
func (s *Service) CreateUser(
|
||||
ctx context.Context,
|
||||
req domain.CreateUserReq,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
)
|
||||
|
||||
type UserStore interface {
|
||||
UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error
|
||||
IsUserPending(ctx context.Context, userName string) (bool, error)
|
||||
GetUserByUserName(
|
||||
ctx context.Context,
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswo
|
|||
return err
|
||||
}
|
||||
|
||||
user, err := s.userStore.GetUserByUserName(ctx, resetReq.UserName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// user, err := s.userStore.GetUserByUserName(ctx, resetReq.UserName)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if otp.Used {
|
||||
return domain.ErrOtpAlreadyUsed
|
||||
|
|
@ -48,7 +48,7 @@ func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswo
|
|||
return domain.ErrInvalidOtp
|
||||
}
|
||||
|
||||
err = s.userStore.UpdatePassword(ctx, resetReq.Password, user.Email, user.PhoneNumber, time.Now())
|
||||
err = s.userStore.UpdatePassword(ctx, resetReq.Password, resetReq.UserName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package httpserver
|
|||
import (
|
||||
"Yimaru-Backend/internal/config"
|
||||
"Yimaru-Backend/internal/services/arifpay"
|
||||
"Yimaru-Backend/internal/services/assessment"
|
||||
"Yimaru-Backend/internal/services/authentication"
|
||||
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
|
||||
notificationservice "Yimaru-Backend/internal/services/notification"
|
||||
|
|
@ -24,25 +25,27 @@ import (
|
|||
)
|
||||
|
||||
type App struct {
|
||||
arifpaySvc *arifpay.ArifpayService
|
||||
assessmentSvc *assessment.Service
|
||||
arifpaySvc *arifpay.ArifpayService
|
||||
issueReportingSvc *issuereporting.Service
|
||||
fiber *fiber.App
|
||||
recommendationSvc recommendation.RecommendationService
|
||||
cfg *config.Config
|
||||
logger *slog.Logger
|
||||
NotidicationStore *notificationservice.Service
|
||||
port int
|
||||
settingSvc *settings.Service
|
||||
authSvc *authentication.Service
|
||||
userSvc *user.Service
|
||||
transactionSvc *transaction.Service
|
||||
validator *customvalidator.CustomValidator
|
||||
JwtConfig jwtutil.JwtConfig
|
||||
Logger *slog.Logger
|
||||
mongoLoggerSvc *zap.Logger
|
||||
port int
|
||||
settingSvc *settings.Service
|
||||
authSvc *authentication.Service
|
||||
userSvc *user.Service
|
||||
transactionSvc *transaction.Service
|
||||
validator *customvalidator.CustomValidator
|
||||
JwtConfig jwtutil.JwtConfig
|
||||
Logger *slog.Logger
|
||||
mongoLoggerSvc *zap.Logger
|
||||
}
|
||||
|
||||
func NewApp(
|
||||
assessmentSvc *assessment.Service,
|
||||
arifpaySvc *arifpay.ArifpayService,
|
||||
issueReportingSvc *issuereporting.Service,
|
||||
port int, validator *customvalidator.CustomValidator,
|
||||
|
|
@ -74,22 +77,23 @@ func NewApp(
|
|||
app.Static("/static", "./static")
|
||||
|
||||
s := &App{
|
||||
arifpaySvc: arifpaySvc,
|
||||
assessmentSvc: assessmentSvc,
|
||||
arifpaySvc: arifpaySvc,
|
||||
// issueReportingSvc: issueReportingSvc,
|
||||
fiber: app,
|
||||
port: port,
|
||||
settingSvc: settingSvc,
|
||||
authSvc: authSvc,
|
||||
validator: validator,
|
||||
logger: logger,
|
||||
JwtConfig: JwtConfig,
|
||||
userSvc: userSvc,
|
||||
transactionSvc: transactionSvc,
|
||||
fiber: app,
|
||||
port: port,
|
||||
settingSvc: settingSvc,
|
||||
authSvc: authSvc,
|
||||
validator: validator,
|
||||
logger: logger,
|
||||
JwtConfig: JwtConfig,
|
||||
userSvc: userSvc,
|
||||
transactionSvc: transactionSvc,
|
||||
NotidicationStore: notidicationStore,
|
||||
Logger: logger,
|
||||
recommendationSvc: recommendationSvc,
|
||||
cfg: cfg,
|
||||
mongoLoggerSvc: mongoLoggerSvc,
|
||||
cfg: cfg,
|
||||
mongoLoggerSvc: mongoLoggerSvc,
|
||||
}
|
||||
|
||||
s.initAppRoutes()
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ package handlers
|
|||
import (
|
||||
"Yimaru-Backend/internal/config"
|
||||
"Yimaru-Backend/internal/services/arifpay"
|
||||
"Yimaru-Backend/internal/services/assessment"
|
||||
"Yimaru-Backend/internal/services/authentication"
|
||||
notificationservice "Yimaru-Backend/internal/services/notification"
|
||||
"Yimaru-Backend/internal/services/recommendation"
|
||||
|
||||
// referralservice "Yimaru-Backend/internal/services/referal"
|
||||
|
||||
"Yimaru-Backend/internal/services/settings"
|
||||
|
|
@ -20,6 +22,7 @@ import (
|
|||
)
|
||||
|
||||
type Handler struct {
|
||||
assessmentSvc *assessment.Service
|
||||
arifpaySvc *arifpay.ArifpayService
|
||||
logger *slog.Logger
|
||||
settingSvc *settings.Service
|
||||
|
|
@ -35,6 +38,7 @@ type Handler struct {
|
|||
}
|
||||
|
||||
func New(
|
||||
assessmentSvc *assessment.Service,
|
||||
arifpaySvc *arifpay.ArifpayService,
|
||||
logger *slog.Logger,
|
||||
settingSvc *settings.Service,
|
||||
|
|
@ -49,6 +53,7 @@ func New(
|
|||
mongoLoggerSvc *zap.Logger,
|
||||
) *Handler {
|
||||
return &Handler{
|
||||
assessmentSvc: assessmentSvc,
|
||||
arifpaySvc: arifpaySvc,
|
||||
logger: logger,
|
||||
settingSvc: settingSvc,
|
||||
|
|
|
|||
142
internal/web_server/handlers/initial_assessment.go
Normal file
142
internal/web_server/handlers/initial_assessment.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"Yimaru-Backend/internal/domain"
|
||||
"Yimaru-Backend/internal/services/authentication"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// CreateAssessmentQuestion godoc
|
||||
// @Summary Create assessment question
|
||||
// @Description Creates a new question for the initial knowledge assessment
|
||||
// @Tags assessment
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param question body domain.AssessmentQuestion true "Assessment question payload"
|
||||
// @Success 201 {object} domain.Response{data=domain.AssessmentQuestion}
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/assessment/questions [post]
|
||||
func (h *Handler) CreateAssessmentQuestion(c *fiber.Ctx) error {
|
||||
var req domain.AssessmentQuestion
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid request body",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
question, err := h.assessmentSvc.CreateAssessmentQuestion(c.Context(), req)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to create assessment question",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
||||
Message: "Assessment question created successfully",
|
||||
Data: question,
|
||||
})
|
||||
}
|
||||
|
||||
// GetActiveAssessmentQuestions godoc
|
||||
// @Summary Get active initial assessment questions
|
||||
// @Description Returns all active questions used for initial knowledge assessment
|
||||
// @Tags assessment
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} domain.Response{data=[]domain.AssessmentQuestion}
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/assessment/questions [get]
|
||||
func (h *Handler) GetActiveAssessmentQuestions(c *fiber.Ctx) error {
|
||||
questions, err := h.assessmentSvc.GetActiveAssessmentQuestions(c.Context())
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to fetch assessment questions",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||
Message: "Assessment questions fetched successfully",
|
||||
Data: questions,
|
||||
})
|
||||
}
|
||||
|
||||
// SubmitAssessment godoc
|
||||
// @Summary Submit initial knowledge assessment
|
||||
// @Description Evaluates user responses, calculates knowledge level, updates user profile, and sends notification
|
||||
// @Tags assessment
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path int true "User ID"
|
||||
// @Param payload body domain.SubmitAssessmentReq true "Assessment responses"
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 404 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/{tenant_slug}/assessment/submit [post]
|
||||
func (h *Handler) SubmitAssessment(c *fiber.Ctx) error {
|
||||
|
||||
// User ID (from auth context or path, depending on your setup)
|
||||
userIDStr, ok := c.Locals("user_id").(string)
|
||||
if !ok || userIDStr == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid user context",
|
||||
Error: "User ID not found in request context",
|
||||
})
|
||||
}
|
||||
|
||||
userID, err := strconv.ParseInt(userIDStr, 10, 64)
|
||||
if err != nil || userID <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid user ID",
|
||||
Error: "User ID must be a positive integer",
|
||||
})
|
||||
}
|
||||
|
||||
// Parse request body
|
||||
var req domain.SubmitAssessmentReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid request body",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if len(req.Answers) == 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "No answers submitted",
|
||||
Error: "Assessment answers cannot be empty",
|
||||
})
|
||||
}
|
||||
|
||||
// Submit assessment
|
||||
attempt, err := h.assessmentSvc.SubmitAssessment(
|
||||
c.Context(),
|
||||
userID,
|
||||
req.Answers,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, authentication.ErrUserNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||
Message: "User not found",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to submit assessment",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||
Message: "Assessment submitted successfully",
|
||||
Data: attempt,
|
||||
})
|
||||
}
|
||||
|
|
@ -13,6 +13,56 @@ import (
|
|||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// UpdateUserKnowledgeLevel godoc
|
||||
// @Summary Update user's knowledge level
|
||||
// @Description Updates the knowledge level of the specified user after initial assessment
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user_id path int true "User ID"
|
||||
// @Param knowledge_level body domain.UpdateKnowledgeLevelReq true "Knowledge level"
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 404 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/{tenant_slug}/user/knowledge-level [put]
|
||||
func (h *Handler) UpdateUserKnowledgeLevel(c *fiber.Ctx) error {
|
||||
userIDStr := c.Locals("user_id").(string)
|
||||
userID, err := strconv.ParseInt(userIDStr, 10, 64)
|
||||
if err != nil || userID <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid user ID",
|
||||
Error: "User ID must be a positive integer",
|
||||
})
|
||||
}
|
||||
|
||||
var req domain.UpdateKnowledgeLevelReq
|
||||
if err := c.BodyParser(&req); err != nil || req.KnowledgeLevel == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid request body",
|
||||
Error: "Knowledge level is required",
|
||||
})
|
||||
}
|
||||
|
||||
err = h.userSvc.UpdateUserKnowledgeLevel(c.Context(), userID, req.KnowledgeLevel)
|
||||
if err != nil {
|
||||
if errors.Is(err, authentication.ErrUserNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||
Message: "User not found",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to update user knowledge level",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||
Message: "User knowledge level updated successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// ResendOtp godoc
|
||||
// @Summary Resend OTP
|
||||
// @Description Resend OTP if the previous one is expired
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
func (a *App) initAppRoutes() {
|
||||
h := handlers.New(
|
||||
a.assessmentSvc,
|
||||
a.arifpaySvc,
|
||||
a.logger,
|
||||
a.settingSvc,
|
||||
|
|
@ -79,6 +80,11 @@ func (a *App) initAppRoutes() {
|
|||
})
|
||||
})
|
||||
|
||||
//assessment Routes
|
||||
groupV1.Post("/assessment/questions", h.CreateAssessmentQuestion)
|
||||
groupV1.Get("/assessment/questions", h.GetActiveAssessmentQuestions)
|
||||
tenant.Post("/assessment/submit", a.authMiddleware, h.SubmitAssessment)
|
||||
|
||||
// Auth Routes
|
||||
tenant.Post("/auth/customer-login", h.LoginUser)
|
||||
tenant.Post("/auth/admin-login", h.LoginAdmin)
|
||||
|
|
@ -122,6 +128,7 @@ func (a *App) initAppRoutes() {
|
|||
// groupV1.Get("/arifpay/payment-methods", a.authMiddleware, h.GetArifpayPaymentMethodsHandler
|
||||
|
||||
// User Routes
|
||||
tenant.Put("/user/knowledge-level", h.UpdateUserKnowledgeLevel)
|
||||
groupV1.Get("/user/:user_name/is-unique", h.CheckUserNameUnique)
|
||||
groupV1.Get("/user/:user_name/is-pending", h.CheckUserPending)
|
||||
groupV1.Post("/user/resetPassword", h.ResetPassword)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user