feat(product-center): implement full product center with NestJS backend, Vue3 admin, and Nuxt4 website page
- NestJS backend: Prisma + PostgreSQL, JWT auth, CRUD for goods/categories/countries/tags/positions, SDS sync with cron, public API, Swagger docs - Vue3 Admin: Element Plus, goods/categories/countries/tags/positions/sync management pages, batch operations, login with JWT - Nuxt4 Website: product-center page with sidebar navigation, country filter, search, product grid, pagination, skeleton loading - Nitro proxy routes for backend API - 54 backend tests passing - Updated docs and README
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SyncType" AS ENUM ('CATEGORIES', 'PRODUCTS');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SyncStatus" AS ENUM ('RUNNING', 'SUCCESS', 'FAILED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "origin_goods" (
|
||||
"origin_good_id" BIGSERIAL NOT NULL,
|
||||
"sds_good_id" TEXT NOT NULL,
|
||||
"sds_category_id" TEXT,
|
||||
"good_name" TEXT,
|
||||
"good_image" TEXT,
|
||||
"good_price" DECIMAL(12,2),
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "origin_goods_pkey" PRIMARY KEY ("origin_good_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "countries" (
|
||||
"country_id" BIGSERIAL NOT NULL,
|
||||
"country_name" TEXT NOT NULL,
|
||||
"country_icon" TEXT,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "countries_pkey" PRIMARY KEY ("country_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "categories" (
|
||||
"category_id" BIGSERIAL NOT NULL,
|
||||
"parent_category_id" BIGINT,
|
||||
"category_name" TEXT NOT NULL,
|
||||
"category_icon" TEXT,
|
||||
"sds_category_id" TEXT,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "categories_pkey" PRIMARY KEY ("category_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tags" (
|
||||
"tag_id" BIGSERIAL NOT NULL,
|
||||
"tag_name" TEXT NOT NULL,
|
||||
"tag_color" TEXT,
|
||||
"timing" TEXT,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "tags_pkey" PRIMARY KEY ("tag_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "positions" (
|
||||
"position_id" BIGSERIAL NOT NULL,
|
||||
"index_val" INTEGER NOT NULL,
|
||||
"country_id" BIGINT,
|
||||
"category_id" BIGINT,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "positions_pkey" PRIMARY KEY ("position_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "goods" (
|
||||
"good_id" BIGSERIAL NOT NULL,
|
||||
"origin_good_id" BIGINT NOT NULL,
|
||||
"country_id" BIGINT NOT NULL,
|
||||
"category_id" BIGINT NOT NULL,
|
||||
"tag_id" BIGINT,
|
||||
"position_id" BIGINT,
|
||||
"good_name" TEXT NOT NULL,
|
||||
"good_priority" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "goods_pkey" PRIMARY KEY ("good_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "users" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"password_hash" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sync_logs" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"type" "SyncType" NOT NULL,
|
||||
"status" "SyncStatus" NOT NULL,
|
||||
"message" TEXT,
|
||||
"started_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"finished_at" TIMESTAMPTZ(6),
|
||||
|
||||
CONSTRAINT "sync_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "origin_goods_sds_good_id_key" ON "origin_goods"("sds_good_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "origin_goods_sds_category_id_idx" ON "origin_goods"("sds_category_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "countries_country_name_key" ON "countries"("country_name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "categories_sds_category_id_key" ON "categories"("sds_category_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "categories_parent_category_id_idx" ON "categories"("parent_category_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tags_tag_name_key" ON "tags"("tag_name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "positions_country_id_idx" ON "positions"("country_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "positions_category_id_idx" ON "positions"("category_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "positions_country_id_category_id_idx" ON "positions"("country_id", "category_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_origin_good_id_idx" ON "goods"("origin_good_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_country_id_idx" ON "goods"("country_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_category_id_idx" ON "goods"("category_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_tag_id_idx" ON "goods"("tag_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_position_id_idx" ON "goods"("position_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_good_priority_idx" ON "goods"("good_priority" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_country_id_good_priority_idx" ON "goods"("country_id", "good_priority" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "goods_category_id_country_id_idx" ON "goods"("category_id", "country_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sync_logs_type_started_at_idx" ON "sync_logs"("type", "started_at");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "categories" ADD CONSTRAINT "categories_parent_category_id_fkey" FOREIGN KEY ("parent_category_id") REFERENCES "categories"("category_id") ON DELETE RESTRICT ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "positions" ADD CONSTRAINT "positions_country_id_fkey" FOREIGN KEY ("country_id") REFERENCES "countries"("country_id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "positions" ADD CONSTRAINT "positions_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "categories"("category_id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "goods" ADD CONSTRAINT "goods_origin_good_id_fkey" FOREIGN KEY ("origin_good_id") REFERENCES "origin_goods"("origin_good_id") ON DELETE RESTRICT ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "goods" ADD CONSTRAINT "goods_country_id_fkey" FOREIGN KEY ("country_id") REFERENCES "countries"("country_id") ON DELETE RESTRICT ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "goods" ADD CONSTRAINT "goods_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "categories"("category_id") ON DELETE RESTRICT ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "goods" ADD CONSTRAINT "goods_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "tags"("tag_id") ON DELETE SET NULL ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "goods" ADD CONSTRAINT "goods_position_id_fkey" FOREIGN KEY ("position_id") REFERENCES "positions"("position_id") ON DELETE SET NULL ON UPDATE NO ACTION;
|
||||
Reference in New Issue
Block a user