diff --git a/configs/config.yaml b/configs/config.yaml index 344c523..796317d 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -17,7 +17,7 @@ debug: database: host: 127.0.0.1 - port: 3306 + port: 5432 user: root password: root name: db_name diff --git a/docs/docs.go b/docs/docs.go index 95a3e4b..47cd9cf 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -40,7 +40,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.Body" }, { "type": "object", @@ -56,7 +56,7 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.Body" } } } @@ -64,6 +64,21 @@ const docTemplate = `{ } }, "definitions": { + "response.Body": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, "schema.CurrentUserResponse": { "type": "object", "properties": { @@ -83,21 +98,6 @@ const docTemplate = `{ "type": "boolean" } } - }, - "schema.ResponseBody": { - "type": "object", - "properties": { - "data": {}, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "success": { - "type": "boolean" - } - } } }, "securityDefinitions": { diff --git a/docs/swagger.json b/docs/swagger.json index 3bbd5cf..e178447 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,101 +1,101 @@ { - "swagger": "2.0", - "info": { - "title": "API Docs", - "contact": {}, - "version": "1.0" - }, - "paths": { - "/api/v1/ping": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "测试接口,将会返回当前用户的信息", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ping" - ], - "summary": "Greet", - "deprecated": true, - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/schema.ResponseBody" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/schema.CurrentUserResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/schema.ResponseBody" - } - } - } - } - } - }, - "definitions": { - "schema.CurrentUserResponse": { - "type": "object", - "properties": { - "ip": { - "type": "string" - }, - "userEmail": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "userName": { - "type": "string" - }, - "valid": { - "type": "boolean" - } - } + "swagger": "2.0", + "info": { + "title": "API Docs", + "contact": {}, + "version": "1.0" }, - "schema.ResponseBody": { - "type": "object", - "properties": { - "data": {}, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "success": { - "type": "boolean" + "paths": { + "/api/v1/ping": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "测试接口,将会返回当前用户的信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ping" + ], + "summary": "Greet", + "deprecated": true, + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Body" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.CurrentUserResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Body" + } + } + } + } + } + }, + "definitions": { + "response.Body": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "schema.CurrentUserResponse": { + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "userEmail": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "valid": { + "type": "boolean" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" } - } } - }, - "securityDefinitions": { - "ApiKeyAuth": { - "type": "apiKey", - "name": "Authorization", - "in": "header" - } - } } \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index fa23649..42be56c 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,4 +1,14 @@ definitions: + response.Body: + properties: + data: {} + error: + type: string + message: + type: string + success: + type: boolean + type: object schema.CurrentUserResponse: properties: ip: @@ -12,48 +22,38 @@ definitions: valid: type: boolean type: object - schema.ResponseBody: - properties: - data: { } - error: - type: string - message: - type: string - success: - type: boolean - type: object info: - contact: { } + contact: {} title: API Docs version: "1.0" paths: /api/v1/ping: get: consumes: - - application/json + - application/json deprecated: true description: 测试接口,将会返回当前用户的信息 produces: - - application/json + - application/json responses: "200": description: OK schema: allOf: - - $ref: '#/definitions/schema.ResponseBody' - - properties: - data: - $ref: '#/definitions/schema.CurrentUserResponse' - type: object + - $ref: '#/definitions/response.Body' + - properties: + data: + $ref: '#/definitions/schema.CurrentUserResponse' + type: object "400": description: Bad Request schema: - $ref: '#/definitions/schema.ResponseBody' + $ref: '#/definitions/response.Body' security: - - ApiKeyAuth: [ ] + - ApiKeyAuth: [] summary: Greet tags: - - ping + - ping securityDefinitions: ApiKeyAuth: in: header diff --git a/internal/handler/http/controller/v1/test.go b/internal/handler/http/controller/v1/test.go index 3648bad..96782f3 100644 --- a/internal/handler/http/controller/v1/test.go +++ b/internal/handler/http/controller/v1/test.go @@ -24,8 +24,8 @@ func NewUserController(authService *auth.Service) *UserController { // @Produce json // @Security ApiKeyAuth // @deprecated true -// @Success 200 {object} schema.ResponseBody{data=schema.CurrentUserResponse} -// @Failure 400 {object} schema.ResponseBody +// @Success 200 {object} response.Body{data=schema.CurrentUserResponse} +// @Failure 400 {object} response.Body // @Router /api/v1/ping [get] func (u *UserController) Test(c echo.Context) error { user, ok := u.authService.GetUser(c) diff --git a/internal/migrations/1_setup.sql b/internal/migrations/1_setup.sql index b28f3d2..9f06bd7 100644 --- a/internal/migrations/1_setup.sql +++ b/internal/migrations/1_setup.sql @@ -1,15 +1,155 @@ -- +goose Up -CREATE TABLE IF NOT EXISTS users +CREATE TABLE apis ( - id BIGSERIAL PRIMARY KEY, - username VARCHAR(255) NOT NULL, - email VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + id BIGSERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, +-- 后端地址 + backend_url VARCHAR(255) NOT NULL, + auth_token VARCHAR(255) NOT NULL, +-- 是否已经验证(验证时间) + ownership_verified_at TIMESTAMP DEFAULT Null, +-- 所有权验证 token + ownership_verification_token VARCHAR(255) NOT NULL, +-- user id string + user_id VARCHAR(255) NOT NULL, +-- 状态 + status VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT now(), + updated_at TIMESTAMP DEFAULT now(), + deleted_at TIMESTAMP DEFAULT Null ); --- +goose Down +-- index +CREATE INDEX idx_api_name ON apis (name); +CREATE INDEX idx_api_user_id ON apis (user_id); +CREATE INDEX idx_api_status ON apis (status); +CREATE INDEX idx_api_deleted_at ON apis (deleted_at); +CREATE INDEX idx_api_ownership_verified_at ON apis (ownership_verified_at); -DROP TABLE IF EXISTS users; + +CREATE TABLE api_documents +( + id BIGSERIAL PRIMARY KEY, + api_id BIGINT NOT NULL, + openapi_url VARCHAR(255) NOT NULL, + content TEXT, + created_at TIMESTAMP DEFAULT now(), + updated_at TIMESTAMP DEFAULT now(), + CONSTRAINT fk_api_id + FOREIGN KEY (api_id) REFERENCES apis (id) +); +CREATE INDEX idx_api_documents_api_id ON api_documents (api_id); +CREATE INDEX idx_api_documents_openapi_url ON api_documents (openapi_url); + +CREATE TABLE packages +( + id BIGSERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + api_id BIGINT NOT NULL, + price DECIMAL(10, 2), +-- hidden + is_hidden BOOLEAN DEFAULT false, + is_published BOOLEAN DEFAULT false, + call_limit BIGINT DEFAULT 10000, +-- 有效期(天) + duration INTEGER DEFAULT 30, +-- 是否为试用套餐 + is_trial BOOLEAN DEFAULT false, +-- 每个用户的购买限制 + purchase_limit INTEGER DEFAULT 1, + created_at TIMESTAMP DEFAULT now(), + updated_at TIMESTAMP DEFAULT now(), + CONSTRAINT fk_api_id FOREIGN KEY (api_id) REFERENCES apis (id), + deleted_at TIMESTAMP DEFAULT Null +); +CREATE INDEX idx_package_name ON packages (name); +CREATE INDEX idx_package_api_id ON packages (api_id); +CREATE INDEX idx_package_price ON packages (price); +CREATE INDEX idx_package_is_published ON packages (is_published); +CREATE INDEX idx_package_is_hidden ON packages (is_hidden); +CREATE INDEX idx_package_is_trial ON packages (is_trial); +CREATE INDEX idx_package_call_limit ON packages (call_limit); +CREATE INDEX idx_package_duration ON packages (duration); +CREATE INDEX idx_package_purchase_limit ON packages (purchase_limit); +CREATE INDEX idx_package_deleted_at ON packages (deleted_at); + +CREATE TABLE user_packages +( + id BIGSERIAL PRIMARY KEY, + user_id VARCHAR(255) NOT NULL, + package_id BIGINT NOT NULL, + calls_left INTEGER DEFAULT 0, + is_active BOOLEAN DEFAULT false, + expiration_at TIMESTAMP DEFAULT NULL, + purchase_at TIMESTAMP NOT NULL, + created_at TIMESTAMP DEFAULT now(), + updated_at TIMESTAMP DEFAULT now(), + CONSTRAINT fk_package_id FOREIGN KEY (package_id) REFERENCES packages (id) +); +CREATE INDEX idx_user_package_user_id ON user_packages (user_id); +CREATE INDEX idx_user_package_package_id ON user_packages (package_id); +CREATE INDEX idx_user_package_is_active ON user_packages (is_active); +CREATE INDEX idx_user_package_expiration_at ON user_packages (expiration_at); +CREATE INDEX idx_user_package_purchase_at ON user_packages (purchase_at); +CREATE INDEX idx_user_package_calls_left ON user_packages (calls_left); + + +CREATE TABLE promo_codes +( + id BIGSERIAL PRIMARY KEY, + code VARCHAR(255) NOT NULL, + discount_type VARCHAR(255) NOT NULL, + discount_value DECIMAL(10, 2) NOT NULL, + expiration_date TIMESTAMP, + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT now(), + updated_at TIMESTAMP DEFAULT now(), + deleted_at TIMESTAMP DEFAULT Null +); +CREATE INDEX idx_promo_code_code ON promo_codes (code); +CREATE INDEX idx_promo_code_discount_type ON promo_codes (discount_type); +CREATE INDEX idx_promo_code_discount_value ON promo_codes (discount_value); +CREATE INDEX idx_promo_code_expiration_date ON promo_codes (expiration_date); +CREATE INDEX idx_promo_code_is_active ON promo_codes (is_active); +CREATE INDEX idx_promo_code_deleted_at ON promo_codes (deleted_at); + +CREATE TABLE orders +( + id BIGSERIAL PRIMARY KEY, + user_id VARCHAR(255) NOT NULL, + promo_code_id BIGINT DEFAULT NULL, + price DECIMAL(10, 2) NOT NULL, + order_date TIMESTAMP NOT NULL, + created_at TIMESTAMP DEFAULT now(), + updated_at TIMESTAMP DEFAULT now(), + CONSTRAINT fk_promo_code_id FOREIGN KEY (promo_code_id) REFERENCES promo_codes (id) ON DELETE SET NULL +); +CREATE INDEX idx_order_user_id ON orders (user_id); +CREATE INDEX idx_order_promo_code_id ON orders (promo_code_id); +CREATE INDEX idx_order_price ON orders (price); +CREATE INDEX idx_order_order_date ON orders (order_date); + + +CREATE TABLE order_items +( + id BIGSERIAL PRIMARY KEY, + order_id BIGINT NOT NULL, + package_id BIGINT NOT NULL, + created_at TIMESTAMP DEFAULT now(), + updated_at TIMESTAMP DEFAULT now(), + CONSTRAINT fk_order_id FOREIGN KEY (order_id) REFERENCES orders (id) +); +CREATE INDEX idx_order_item_order_id ON order_items (order_id); +CREATE INDEX idx_order_item_package_id ON order_items (package_id); + +-- +goose Down +DROP TABLE IF EXISTS order_items; +DROP TABLE IF EXISTS orders; +DROP TABLE IF EXISTS promo_codes; +DROP TABLE IF EXISTS user_packages; +DROP TABLE IF EXISTS packages; +DROP TABLE IF EXISTS api_documents; +DROP TABLE IF EXISTS apis;