From b79f501b0034641895db6469450aa8e13b0a97ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E6=98=95=E7=9D=BF?= <22371298@buaa.edu.cn> Date: Sun, 20 Apr 2025 21:17:13 +0800 Subject: [PATCH] =?UTF-8?q?[feat]:=20=E6=96=87=E7=8C=AE=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=9A=84=E5=9F=BA=E6=9C=AC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alembic.ini | 2 +- ...66\351\227\264\344\277\241\346\201\257.py" | 42 ----- ...4_definition_of_all_entity_and_relation.py | 77 --------- ...17\351\273\230\350\256\244\345\200\274.py" | 32 ---- ...6\351\227\264\344\277\241\346\201\2572.py" | 38 ----- .../56ae02842433_definition_of_all_entity.py | 46 ----- alembic/versions/5c562ce917d3_init.py | 44 ----- ...63\347\263\273\345\256\232\344\271\211.py" | 141 ---------------- ...73\345\212\240\347\272\246\346\235\237.py" | 32 ---- ...33\264\346\224\271password\345\220\215.py" | 34 ---- ...63\347\263\273\345\256\232\344\271\211.py" | 40 ----- ...56\345\272\223\345\256\232\344\271\211.py" | 34 ---- ...13\346\225\260\346\215\256\345\272\223.py" | 44 ----- ...66\347\253\231\346\224\257\346\214\201.py" | 18 +- ...44\270\272text\345\255\230\345\202\250.py" | 38 ----- alembic/versions/e911f584a9c0_init.py | 42 ----- ...66\345\214\272\346\222\244\351\224\200.py" | 32 ---- app/api/v1/endpoints/article.py | 158 ++++++++++++++++++ app/core/config.py | 2 +- app/curd/article.py | 100 +++++++++++ app/models/model.py | 4 + app/routers/router.py | 7 +- app/schemas/article.py | 1 + requirements.txt | Bin 841 -> 1380 bytes 24 files changed, 281 insertions(+), 727 deletions(-) delete mode 100644 "alembic/versions/1791499b5d10_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\257.py" delete mode 100644 alembic/versions/1bf7c40393d4_definition_of_all_entity_and_relation.py delete mode 100644 "alembic/versions/2acf0b902f73_\346\267\273\345\212\240\345\244\264\345\203\217\351\273\230\350\256\244\345\200\274.py" delete mode 100644 "alembic/versions/4df692d79c60_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\2572.py" delete mode 100644 alembic/versions/56ae02842433_definition_of_all_entity.py delete mode 100644 alembic/versions/5c562ce917d3_init.py delete mode 100644 "alembic/versions/5dc79cf05715_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" delete mode 100644 "alembic/versions/698cace619a6_\346\267\273\345\212\240\347\272\246\346\235\237.py" delete mode 100644 "alembic/versions/9af9d4a35bef_fix_\346\233\264\346\224\271password\345\220\215.py" delete mode 100644 "alembic/versions/b7940480e6e6_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" delete mode 100644 "alembic/versions/c49010e96150_fix_\346\225\260\346\215\256\345\272\223\345\256\232\344\271\211.py" delete mode 100644 "alembic/versions/c976ca5c1d8f_\344\275\277\347\224\250\350\277\234\347\250\213\346\225\260\346\215\256\345\272\223.py" rename "alembic/versions/d60fdc9865f8_\346\224\271\345\217\230\346\227\266\345\214\272.py" => "alembic/versions/cf83488540d9_\346\226\207\347\214\256\345\222\214\346\226\207\344\273\266\345\244\271\347\232\204\345\233\236\346\224\266\347\253\231\346\224\257\346\214\201.py" (55%) delete mode 100644 "alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" delete mode 100644 alembic/versions/e911f584a9c0_init.py delete mode 100644 "alembic/versions/f1242bbcad2d_\346\224\271\345\217\230\346\227\266\345\214\272\346\222\244\351\224\200.py" create mode 100644 app/api/v1/endpoints/article.py create mode 100644 app/curd/article.py create mode 100644 app/schemas/article.py diff --git a/alembic.ini b/alembic.ini index fa56891..c44a966 100644 --- a/alembic.ini +++ b/alembic.ini @@ -63,7 +63,7 @@ version_path_separator = os # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = mysql+pymysql://root:coders007@47.93.172.156:3306/JieNote +sqlalchemy.url = mysql+pymysql://root:oneapi@47.93.172.156:3306/JieNote [post_write_hooks] diff --git "a/alembic/versions/1791499b5d10_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\257.py" "b/alembic/versions/1791499b5d10_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\257.py" deleted file mode 100644 index 92c39f9..0000000 --- "a/alembic/versions/1791499b5d10_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\257.py" +++ /dev/null @@ -1,42 +0,0 @@ -"""增加时间信息 - -Revision ID: 1791499b5d10 -Revises: e04ed2119f01 -Create Date: 2025-04-13 10:20:44.770496 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '1791499b5d10' -down_revision: Union[str, None] = 'e04ed2119f01' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('articles', sa.Column('create_time', sa.DateTime(), nullable=False)) - op.add_column('articles', sa.Column('update_time', sa.DateTime(), nullable=False)) - op.add_column('folders', sa.Column('create_time', sa.DateTime(), nullable=False)) - op.add_column('folders', sa.Column('update_time', sa.DateTime(), nullable=False)) - op.add_column('notes', sa.Column('create_time', sa.DateTime(), nullable=False)) - op.add_column('notes', sa.Column('update_time', sa.DateTime(), nullable=False)) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('notes', 'update_time') - op.drop_column('notes', 'create_time') - op.drop_column('folders', 'update_time') - op.drop_column('folders', 'create_time') - op.drop_column('articles', 'update_time') - op.drop_column('articles', 'create_time') - # ### end Alembic commands ### diff --git a/alembic/versions/1bf7c40393d4_definition_of_all_entity_and_relation.py b/alembic/versions/1bf7c40393d4_definition_of_all_entity_and_relation.py deleted file mode 100644 index 8c6e9f2..0000000 --- a/alembic/versions/1bf7c40393d4_definition_of_all_entity_and_relation.py +++ /dev/null @@ -1,77 +0,0 @@ -"""definition of all entity and relation - -Revision ID: 1bf7c40393d4 -Revises: 56ae02842433 -Create Date: 2025-04-10 10:44:24.487467 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '1bf7c40393d4' -down_revision: Union[str, None] = '56ae02842433' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('articlenotes', - sa.Column('articleid', sa.Integer(), nullable=True), - sa.Column('noteid', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('noteid') - ) - op.create_index(op.f('ix_articlenotes_noteid'), 'articlenotes', ['noteid'], unique=False) - op.create_table('articlesetarticles', - sa.Column('articlesetid', sa.Integer(), nullable=True), - sa.Column('articleid', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('articleid') - ) - op.create_index(op.f('ix_articlesetarticles_articleid'), 'articlesetarticles', ['articleid'], unique=False) - op.create_table('articletags', - sa.Column('articleid', sa.Integer(), nullable=True), - sa.Column('tagid', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('tagid') - ) - op.create_index(op.f('ix_articletags_tagid'), 'articletags', ['tagid'], unique=False) - op.create_table('grouparticlesets', - sa.Column('groupid', sa.Integer(), nullable=True), - sa.Column('articlesetid', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('articlesetid') - ) - op.create_index(op.f('ix_grouparticlesets_articlesetid'), 'grouparticlesets', ['articlesetid'], unique=False) - op.create_table('userarticlesets', - sa.Column('userid', sa.Integer(), nullable=True), - sa.Column('articlesetid', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('articlesetid') - ) - op.create_index(op.f('ix_userarticlesets_articlesetid'), 'userarticlesets', ['articlesetid'], unique=False) - op.create_table('usergroups', - sa.Column('userid', sa.Integer(), nullable=False), - sa.Column('groupid', sa.Integer(), nullable=False), - sa.Column('isadmin', sa.Boolean(), nullable=True), - sa.PrimaryKeyConstraint('userid', 'groupid') - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('usergroups') - op.drop_index(op.f('ix_userarticlesets_articlesetid'), table_name='userarticlesets') - op.drop_table('userarticlesets') - op.drop_index(op.f('ix_grouparticlesets_articlesetid'), table_name='grouparticlesets') - op.drop_table('grouparticlesets') - op.drop_index(op.f('ix_articletags_tagid'), table_name='articletags') - op.drop_table('articletags') - op.drop_index(op.f('ix_articlesetarticles_articleid'), table_name='articlesetarticles') - op.drop_table('articlesetarticles') - op.drop_index(op.f('ix_articlenotes_noteid'), table_name='articlenotes') - op.drop_table('articlenotes') - # ### end Alembic commands ### diff --git "a/alembic/versions/2acf0b902f73_\346\267\273\345\212\240\345\244\264\345\203\217\351\273\230\350\256\244\345\200\274.py" "b/alembic/versions/2acf0b902f73_\346\267\273\345\212\240\345\244\264\345\203\217\351\273\230\350\256\244\345\200\274.py" deleted file mode 100644 index f9dca8a..0000000 --- "a/alembic/versions/2acf0b902f73_\346\267\273\345\212\240\345\244\264\345\203\217\351\273\230\350\256\244\345\200\274.py" +++ /dev/null @@ -1,32 +0,0 @@ -"""添加头像默认值 - -Revision ID: 2acf0b902f73 -Revises: b7940480e6e6 -Create Date: 2025-04-11 22:54:09.734172 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '2acf0b902f73' -down_revision: Union[str, None] = 'b7940480e6e6' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### diff --git "a/alembic/versions/4df692d79c60_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\2572.py" "b/alembic/versions/4df692d79c60_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\2572.py" deleted file mode 100644 index 2575376..0000000 --- "a/alembic/versions/4df692d79c60_\345\242\236\345\212\240\346\227\266\351\227\264\344\277\241\346\201\2572.py" +++ /dev/null @@ -1,38 +0,0 @@ -"""增加时间信息2 - -Revision ID: 4df692d79c60 -Revises: 1791499b5d10 -Create Date: 2025-04-13 10:25:25.139263 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '4df692d79c60' -down_revision: Union[str, None] = '1791499b5d10' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('groups', sa.Column('create_time', sa.DateTime(), nullable=False)) - op.add_column('groups', sa.Column('update_time', sa.DateTime(), nullable=False)) - op.add_column('tags', sa.Column('create_time', sa.DateTime(), nullable=False)) - op.add_column('tags', sa.Column('update_time', sa.DateTime(), nullable=False)) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('tags', 'update_time') - op.drop_column('tags', 'create_time') - op.drop_column('groups', 'update_time') - op.drop_column('groups', 'create_time') - # ### end Alembic commands ### diff --git a/alembic/versions/56ae02842433_definition_of_all_entity.py b/alembic/versions/56ae02842433_definition_of_all_entity.py deleted file mode 100644 index 0cf41da..0000000 --- a/alembic/versions/56ae02842433_definition_of_all_entity.py +++ /dev/null @@ -1,46 +0,0 @@ -"""definition of all entity - -Revision ID: 56ae02842433 -Revises: c976ca5c1d8f -Create Date: 2025-04-10 00:04:27.588283 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision: str = '56ae02842433' -down_revision: Union[str, None] = 'c976ca5c1d8f' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('email', sa.String(length=30), nullable=False)) - op.add_column('users', sa.Column('username', sa.String(length=30), nullable=False)) - op.drop_index('ix_users_teacher_name', table_name='users') - op.drop_index('ix_users_work_number', table_name='users') - op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True) - op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=False) - op.drop_column('users', 'teacher_name') - op.drop_column('users', 'work_number') - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('work_number', mysql.INTEGER(), autoincrement=False, nullable=False)) - op.add_column('users', sa.Column('teacher_name', mysql.VARCHAR(length=20), nullable=False)) - op.drop_index(op.f('ix_users_username'), table_name='users') - op.drop_index(op.f('ix_users_email'), table_name='users') - op.create_index('ix_users_work_number', 'users', ['work_number'], unique=False) - op.create_index('ix_users_teacher_name', 'users', ['teacher_name'], unique=False) - op.drop_column('users', 'username') - op.drop_column('users', 'email') - # ### end Alembic commands ### diff --git a/alembic/versions/5c562ce917d3_init.py b/alembic/versions/5c562ce917d3_init.py deleted file mode 100644 index da6aaf7..0000000 --- a/alembic/versions/5c562ce917d3_init.py +++ /dev/null @@ -1,44 +0,0 @@ -"""init - -Revision ID: 5c562ce917d3 -Revises: e911f584a9c0 -Create Date: 2025-04-09 10:20:46.223969 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '5c562ce917d3' -down_revision: Union[str, None] = 'e911f584a9c0' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('users', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('work_number', sa.Integer(), nullable=False), - sa.Column('hash_password', sa.String(length=60), nullable=False), - sa.Column('teacher_name', sa.String(length=20), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False) - op.create_index(op.f('ix_users_teacher_name'), 'users', ['teacher_name'], unique=False) - op.create_index(op.f('ix_users_work_number'), 'users', ['work_number'], unique=False) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_users_work_number'), table_name='users') - op.drop_index(op.f('ix_users_teacher_name'), table_name='users') - op.drop_index(op.f('ix_users_id'), table_name='users') - op.drop_table('users') - # ### end Alembic commands ### diff --git "a/alembic/versions/5dc79cf05715_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" "b/alembic/versions/5dc79cf05715_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" deleted file mode 100644 index cacd6d0..0000000 --- "a/alembic/versions/5dc79cf05715_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" +++ /dev/null @@ -1,141 +0,0 @@ -"""添加数据库实体关系定义 - -Revision ID: 5dc79cf05715 -Revises: 1bf7c40393d4 -Create Date: 2025-04-11 20:59:07.250741 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision: str = '5dc79cf05715' -down_revision: Union[str, None] = '1bf7c40393d4' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('folders', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('name', sa.String(length=30), nullable=True), - sa.Column('user_id', sa.Integer(), nullable=True), - sa.Column('groups_id', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['groups_id'], ['groups.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), - sa.PrimaryKeyConstraint('id') - ) - op.create_index(op.f('ix_folders_id'), 'folders', ['id'], unique=False) - op.create_table('user_group', - sa.Column('user_id', sa.Integer(), nullable=False), - sa.Column('group_id', sa.Integer(), nullable=False), - sa.Column('is_admin', sa.Boolean(), nullable=True), - sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), - sa.PrimaryKeyConstraint('user_id', 'group_id') - ) - op.drop_index('ix_articlesetarticles_articleid', table_name='articlesetarticles') - op.drop_table('articlesetarticles') - op.drop_index('ix_articletags_tagid', table_name='articletags') - op.drop_table('articletags') - op.drop_index('ix_userarticlesets_articlesetid', table_name='userarticlesets') - op.drop_table('userarticlesets') - op.drop_index('ix_grouparticlesets_articlesetid', table_name='grouparticlesets') - op.drop_table('grouparticlesets') - op.drop_index('ix_articlesets_id', table_name='articlesets') - op.drop_table('articlesets') - op.drop_index('ix_articlenotes_noteid', table_name='articlenotes') - op.drop_table('articlenotes') - op.drop_table('usergroups') - op.add_column('articles', sa.Column('folder_id', sa.Integer(), nullable=True)) - op.create_foreign_key(None, 'articles', 'folders', ['folder_id'], ['id']) - op.add_column('notes', sa.Column('article_id', sa.Integer(), nullable=True)) - op.create_foreign_key(None, 'notes', 'articles', ['article_id'], ['id']) - op.add_column('tags', sa.Column('article_id', sa.Integer(), nullable=True)) - op.create_foreign_key(None, 'tags', 'articles', ['article_id'], ['id']) - op.add_column('users', sa.Column('avatar', sa.String(length=100), nullable=True)) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('users', 'avatar') - op.drop_constraint(None, 'tags', type_='foreignkey') - op.drop_column('tags', 'article_id') - op.drop_constraint(None, 'notes', type_='foreignkey') - op.drop_column('notes', 'article_id') - op.drop_constraint(None, 'articles', type_='foreignkey') - op.drop_column('articles', 'folder_id') - op.create_table('usergroups', - sa.Column('userid', mysql.INTEGER(), autoincrement=False, nullable=False), - sa.Column('groupid', mysql.INTEGER(), autoincrement=False, nullable=False), - sa.Column('isadmin', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True), - sa.PrimaryKeyConstraint('userid', 'groupid'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_table('articlenotes', - sa.Column('articleid', mysql.INTEGER(), autoincrement=False, nullable=True), - sa.Column('noteid', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.PrimaryKeyConstraint('noteid'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_articlenotes_noteid', 'articlenotes', ['noteid'], unique=False) - op.create_table('articlesets', - sa.Column('id', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.Column('name', mysql.VARCHAR(length=30), nullable=True), - sa.PrimaryKeyConstraint('id'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_articlesets_id', 'articlesets', ['id'], unique=False) - op.create_table('grouparticlesets', - sa.Column('groupid', mysql.INTEGER(), autoincrement=False, nullable=True), - sa.Column('articlesetid', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.PrimaryKeyConstraint('articlesetid'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_grouparticlesets_articlesetid', 'grouparticlesets', ['articlesetid'], unique=False) - op.create_table('userarticlesets', - sa.Column('userid', mysql.INTEGER(), autoincrement=False, nullable=True), - sa.Column('articlesetid', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.PrimaryKeyConstraint('articlesetid'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_userarticlesets_articlesetid', 'userarticlesets', ['articlesetid'], unique=False) - op.create_table('articletags', - sa.Column('articleid', mysql.INTEGER(), autoincrement=False, nullable=True), - sa.Column('tagid', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.PrimaryKeyConstraint('tagid'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_articletags_tagid', 'articletags', ['tagid'], unique=False) - op.create_table('articlesetarticles', - sa.Column('articlesetid', mysql.INTEGER(), autoincrement=False, nullable=True), - sa.Column('articleid', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.PrimaryKeyConstraint('articleid'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_articlesetarticles_articleid', 'articlesetarticles', ['articleid'], unique=False) - op.drop_table('user_group') - op.drop_index(op.f('ix_folders_id'), table_name='folders') - op.drop_table('folders') - # ### end Alembic commands ### diff --git "a/alembic/versions/698cace619a6_\346\267\273\345\212\240\347\272\246\346\235\237.py" "b/alembic/versions/698cace619a6_\346\267\273\345\212\240\347\272\246\346\235\237.py" deleted file mode 100644 index 8b2d594..0000000 --- "a/alembic/versions/698cace619a6_\346\267\273\345\212\240\347\272\246\346\235\237.py" +++ /dev/null @@ -1,32 +0,0 @@ -"""添加约束 - -Revision ID: 698cace619a6 -Revises: f1242bbcad2d -Create Date: 2025-04-18 17:18:55.547867 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '698cace619a6' -down_revision: Union[str, None] = 'f1242bbcad2d' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### diff --git "a/alembic/versions/9af9d4a35bef_fix_\346\233\264\346\224\271password\345\220\215.py" "b/alembic/versions/9af9d4a35bef_fix_\346\233\264\346\224\271password\345\220\215.py" deleted file mode 100644 index 2f52761..0000000 --- "a/alembic/versions/9af9d4a35bef_fix_\346\233\264\346\224\271password\345\220\215.py" +++ /dev/null @@ -1,34 +0,0 @@ -"""fix:更改password名 - -Revision ID: 9af9d4a35bef -Revises: c49010e96150 -Create Date: 2025-04-12 10:27:52.832186 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision: str = '9af9d4a35bef' -down_revision: Union[str, None] = 'c49010e96150' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('password', sa.String(length=60), nullable=False)) - op.drop_column('users', 'hash_password') - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('hash_password', mysql.VARCHAR(length=60), nullable=False)) - op.drop_column('users', 'password') - # ### end Alembic commands ### diff --git "a/alembic/versions/b7940480e6e6_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" "b/alembic/versions/b7940480e6e6_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" deleted file mode 100644 index 1b082cb..0000000 --- "a/alembic/versions/b7940480e6e6_\346\267\273\345\212\240\346\225\260\346\215\256\345\272\223\345\256\236\344\275\223\345\205\263\347\263\273\345\256\232\344\271\211.py" +++ /dev/null @@ -1,40 +0,0 @@ -"""添加数据库实体关系定义 - -Revision ID: b7940480e6e6 -Revises: 5dc79cf05715 -Create Date: 2025-04-11 21:10:47.613089 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision: str = 'b7940480e6e6' -down_revision: Union[str, None] = '5dc79cf05715' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('folders', sa.Column('group_id', sa.Integer(), nullable=True)) - op.create_unique_constraint('uq_user_group_folder', 'folders', ['user_id', 'group_id']) - op.drop_constraint('folders_ibfk_1', 'folders', type_='foreignkey') - op.create_foreign_key(None, 'folders', 'groups', ['group_id'], ['id']) - op.drop_column('folders', 'groups_id') - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('folders', sa.Column('groups_id', mysql.INTEGER(), autoincrement=False, nullable=True)) - op.drop_constraint(None, 'folders', type_='foreignkey') - op.create_foreign_key('folders_ibfk_1', 'folders', 'groups', ['groups_id'], ['id']) - op.drop_constraint('uq_user_group_folder', 'folders', type_='unique') - op.drop_column('folders', 'group_id') - # ### end Alembic commands ### diff --git "a/alembic/versions/c49010e96150_fix_\346\225\260\346\215\256\345\272\223\345\256\232\344\271\211.py" "b/alembic/versions/c49010e96150_fix_\346\225\260\346\215\256\345\272\223\345\256\232\344\271\211.py" deleted file mode 100644 index 5c4fa4e..0000000 --- "a/alembic/versions/c49010e96150_fix_\346\225\260\346\215\256\345\272\223\345\256\232\344\271\211.py" +++ /dev/null @@ -1,34 +0,0 @@ -"""fix 数据库定义 - -Revision ID: c49010e96150 -Revises: 2acf0b902f73 -Create Date: 2025-04-12 10:19:29.708681 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision: str = 'c49010e96150' -down_revision: Union[str, None] = '2acf0b902f73' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('notes', sa.Column('content', sa.String(length=255), nullable=True)) - op.drop_column('notes', 'name') - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('notes', sa.Column('name', mysql.VARCHAR(length=30), nullable=True)) - op.drop_column('notes', 'content') - # ### end Alembic commands ### diff --git "a/alembic/versions/c976ca5c1d8f_\344\275\277\347\224\250\350\277\234\347\250\213\346\225\260\346\215\256\345\272\223.py" "b/alembic/versions/c976ca5c1d8f_\344\275\277\347\224\250\350\277\234\347\250\213\346\225\260\346\215\256\345\272\223.py" deleted file mode 100644 index f27967b..0000000 --- "a/alembic/versions/c976ca5c1d8f_\344\275\277\347\224\250\350\277\234\347\250\213\346\225\260\346\215\256\345\272\223.py" +++ /dev/null @@ -1,44 +0,0 @@ -"""使用远程数据库 - -Revision ID: c976ca5c1d8f -Revises: 5c562ce917d3 -Create Date: 2025-04-09 21:33:43.133516 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'c976ca5c1d8f' -down_revision: Union[str, None] = '5c562ce917d3' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('users', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('work_number', sa.Integer(), nullable=False), - sa.Column('hash_password', sa.String(length=60), nullable=False), - sa.Column('teacher_name', sa.String(length=20), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False) - op.create_index(op.f('ix_users_teacher_name'), 'users', ['teacher_name'], unique=False) - op.create_index(op.f('ix_users_work_number'), 'users', ['work_number'], unique=False) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_users_work_number'), table_name='users') - op.drop_index(op.f('ix_users_teacher_name'), table_name='users') - op.drop_index(op.f('ix_users_id'), table_name='users') - op.drop_table('users') - # ### end Alembic commands ### diff --git "a/alembic/versions/d60fdc9865f8_\346\224\271\345\217\230\346\227\266\345\214\272.py" "b/alembic/versions/cf83488540d9_\346\226\207\347\214\256\345\222\214\346\226\207\344\273\266\345\244\271\347\232\204\345\233\236\346\224\266\347\253\231\346\224\257\346\214\201.py" similarity index 55% rename from "alembic/versions/d60fdc9865f8_\346\224\271\345\217\230\346\227\266\345\214\272.py" rename to "alembic/versions/cf83488540d9_\346\226\207\347\214\256\345\222\214\346\226\207\344\273\266\345\244\271\347\232\204\345\233\236\346\224\266\347\253\231\346\224\257\346\214\201.py" index 8f4252d..9fa13d9 100644 --- "a/alembic/versions/d60fdc9865f8_\346\224\271\345\217\230\346\227\266\345\214\272.py" +++ "b/alembic/versions/cf83488540d9_\346\226\207\347\214\256\345\222\214\346\226\207\344\273\266\345\244\271\347\232\204\345\233\236\346\224\266\347\253\231\346\224\257\346\214\201.py" @@ -1,8 +1,8 @@ -"""改变时区" +"""文献和文件夹的回收站支持 -Revision ID: d60fdc9865f8 -Revises: 4df692d79c60 -Create Date: 2025-04-13 10:35:50.120925 +Revision ID: cf83488540d9 +Revises: +Create Date: 2025-04-20 21:08:32.574488 """ from typing import Sequence, Union @@ -12,8 +12,8 @@ # revision identifiers, used by Alembic. -revision: str = 'd60fdc9865f8' -down_revision: Union[str, None] = '4df692d79c60' +revision: str = 'cf83488540d9' +down_revision: Union[str, None] = None branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None @@ -21,12 +21,14 @@ def upgrade() -> None: """Upgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - pass + op.add_column('articles', sa.Column('visible', sa.Boolean(), nullable=False)) + op.add_column('folders', sa.Column('visible', sa.Boolean(), nullable=False)) # ### end Alembic commands ### def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - pass + op.drop_column('folders', 'visible') + op.drop_column('articles', 'visible') # ### end Alembic commands ### diff --git "a/alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" "b/alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" deleted file mode 100644 index 5b55eac..0000000 --- "a/alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" +++ /dev/null @@ -1,38 +0,0 @@ -"""将笔记内容变为text存储 - -Revision ID: e04ed2119f01 -Revises: 9af9d4a35bef -Create Date: 2025-04-12 15:42:31.823878 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision: str = 'e04ed2119f01' -down_revision: Union[str, None] = '9af9d4a35bef' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('notes', 'content', - existing_type=mysql.VARCHAR(length=255), - type_=sa.Text(), - existing_nullable=True) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('notes', 'content', - existing_type=sa.Text(), - type_=mysql.VARCHAR(length=255), - existing_nullable=True) - # ### end Alembic commands ### diff --git a/alembic/versions/e911f584a9c0_init.py b/alembic/versions/e911f584a9c0_init.py deleted file mode 100644 index 9114c37..0000000 --- a/alembic/versions/e911f584a9c0_init.py +++ /dev/null @@ -1,42 +0,0 @@ -"""init - -Revision ID: e911f584a9c0 -Revises: -Create Date: 2025-04-09 10:17:37.889061 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision: str = 'e911f584a9c0' -down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index('ix_examples_id', table_name='examples') - op.drop_table('examples') - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('examples', - sa.Column('id', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.Column('name', mysql.VARCHAR(length=50), nullable=False), - sa.Column('description', mysql.VARCHAR(length=255), nullable=True), - sa.PrimaryKeyConstraint('id'), - mysql_collate='utf8mb4_0900_ai_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_examples_id', 'examples', ['id'], unique=False) - # ### end Alembic commands ### diff --git "a/alembic/versions/f1242bbcad2d_\346\224\271\345\217\230\346\227\266\345\214\272\346\222\244\351\224\200.py" "b/alembic/versions/f1242bbcad2d_\346\224\271\345\217\230\346\227\266\345\214\272\346\222\244\351\224\200.py" deleted file mode 100644 index b4cdf97..0000000 --- "a/alembic/versions/f1242bbcad2d_\346\224\271\345\217\230\346\227\266\345\214\272\346\222\244\351\224\200.py" +++ /dev/null @@ -1,32 +0,0 @@ -"""改变时区撤销 - -Revision ID: f1242bbcad2d -Revises: d60fdc9865f8 -Create Date: 2025-04-13 10:38:58.146909 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'f1242bbcad2d' -down_revision: Union[str, None] = 'd60fdc9865f8' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### diff --git a/app/api/v1/endpoints/article.py b/app/api/v1/endpoints/article.py new file mode 100644 index 0000000..9cdb1dc --- /dev/null +++ b/app/api/v1/endpoints/article.py @@ -0,0 +1,158 @@ +from fastapi import APIRouter, UploadFile, File, Query, Depends, HTTPException, Body +from fastapi.responses import FileResponse +from sqlalchemy.ext.asyncio import AsyncSession +from typing import Optional +import os + +from utils.get_db import get_db +from utils.auth import get_current_user +from curd.user import get_user_by_email +from curd.article import crud_upload_to_self_folder, crud_get_self_folders, crud_get_articles_in_folder, crud_self_create_folder, crud_self_article_to_recycle_bin, crud_self_folder_to_recycle_bin, crud_read_article + +router = APIRouter() + +@router.post("/uploadToSelfFolder", response_model="dict") +async def upload_to_self_folder(folder_id: int = Query(...), article: UploadFile = File(...), db: AsyncSession = Depends(get_db)): + # 检查是否为 PDF 文件 + if article.content_type != "application/pdf": + raise HTTPException(status_code=400, detail="Only PDF files are allowed.") + + # 用文件名(不带扩展名)作为 Article 名称 + name = os.path.splitext(article.filename)[0] + + # 新建 Article 记录 + article_id = await crud_upload_to_self_folder(name, folder_id, db) + + # 存储文件,暂时存储到本地 + save_dir = "articles" + os.makedirs(save_dir, exist_ok=True) # 如果目录不存在则创建 + save_path = os.path.join(save_dir, f"{article_id}.pdf") + with open(save_path, "wb") as f: + content = await article.read() + f.write(content) + + return {"msg": "Article created successfully."} + +@router.get("/getSelfFolders", response_model="dict") +async def get_self_folders(page_number: Optional[int] = Query(None, ge=1), page_size: Optional[int] = Query(None, ge=1), + db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): + # 获取用户邮箱 + user_email = user.get("email") + if not user_email: + raise HTTPException(status_code=401, detail="Unauthorized") + + # 由邮箱查得id + db_user = await get_user_by_email(db, user_email) + if not db_user: + raise HTTPException(status_code=404, detail="User not found") + user_id = db_user.id + + # 数据库查询 + folders = await crud_get_self_folders(user_id, page_number, page_size, db) + + # 返回结果 + result = [{"folder_id": folder.id, "folder_name": folder.name} for folder in folders] + return {"result": result} + +@router.get("/getArticlesInFolder", response_model="dict") +async def get_articles_in_folder(folder_id: int = Query(...), page_number: Optional[int] = Query(None, ge=1), page_size: Optional[int] = Query(None, ge=1), + db: AsyncSession = Depends(get_db)): + articles = await crud_get_articles_in_folder(folder_id, page_number, page_size, db) + result = [{"article_id": article.id, "article_name": article.name} for article in articles] + return {"result": result} + +@router.post("/selfCreateFolder", response_model="dict") +async def self_create_folder(folder_name: str = Body(...), db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): + if folder_name == "": + raise HTTPException(status_code=405, detail="Empty Folder Name") + + # 获取用户邮箱 + user_email = user.get("email") + if not user_email: + raise HTTPException(status_code=401, detail="Unauthorized") + + # 由邮箱查得id + db_user = await get_user_by_email(db, user_email) + if not db_user: + raise HTTPException(status_code=404, detail="User not found") + user_id = db_user.id + + # 数据库插入 + await crud_self_create_folder(folder_name, user_id, db) + + # 返回结果 + return {"msg": "User Folder Created Successfully"} + +@router.delete("/selfArticleToRecycleBin", resplonse_model="dict") +async def self_article_to_recycle_bin(article_id: int = Query(...), db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): + # 获取用户邮箱 + user_email = user.get("email") + if not user_email: + raise HTTPException(status_code=401, detail="Unauthorized") + + # 由邮箱查得id + db_user = await get_user_by_email(db, user_email) + if not db_user: + raise HTTPException(status_code=404, detail="User not found") + user_id = db_user.id + + # 数据库修改 + await crud_self_article_to_recycle_bin(article_id, user_id, db) + + # 返回结果 + return {"msg": "Article is moved to recycle bin"} + +@router.delete("/selfFolderToRecycleBin", response_model="dict") +async def self_folder_to_recycle_bin(folder_id: int = Query(...), db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): + # 获取用户邮箱 + user_email = user.get("email") + if not user_email: + raise HTTPException(status_code=401, detail="Unauthorized") + + # 由邮箱查得id + db_user = await get_user_by_email(db, user_email) + if not db_user: + raise HTTPException(status_code=404, detail="User not found") + user_id = db_user.id + + # 数据库修改 + await crud_self_folder_to_recycle_bin(folder_id, user_id, db) + + # 返回结果 + return {"msg": "Folder is moved to recycle bin"} + +@router.post("/annotateSelfArticle", response_model="dict") +async def annotate_self_article(article_id: int = Query(...), article: UploadFile = File(...)): + # 存储文件,将新文件暂时存储到本地 + save_dir = "articles" + os.makedirs(save_dir, exist_ok=True) # 如果目录不存在则创建 + save_path = os.path.join(save_dir, f"{article_id}.pdf") + with open(save_path, "wb") as f: + content = await article.read() + f.write(content) + + return {"msg": "Article annotated successfully."} + +@router.get("/readArticle", response_class=FileResponse) +async def read_article(article_id: int = Query(...), db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): + # 获取用户邮箱 + user_email = user.get("email") + if not user_email: + raise HTTPException(status_code=401, detail="Unauthorized") + + # 由邮箱查得id + db_user = await get_user_by_email(db, user_email) + if not db_user: + raise HTTPException(status_code=404, detail="User not found") + user_id = db_user.id + + # 文件路径 + file_path = f"articles/{article_id}.pdf" + if not os.path.exists(file_path): + raise HTTPException(status_code=404, detail="PDF file not found") + + # 查询文件名 + article_name = await crud_read_article(article_id, user_id, db) + + # 返回结果 + return FileResponse(path=file_path, filename=f"{article_name}.pdf", media_type='application/pdf') \ No newline at end of file diff --git a/app/core/config.py b/app/core/config.py index 5e13cf9..acef1dd 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -6,7 +6,7 @@ class Settings: PROJECT_NAME: str = "JieNote Backend" # 项目名称 VERSION: str = "1.0.0" # 项目版本 - SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://root:coders007@47.93.172.156:3306/JieNote" # 替换为实际的用户名、密码和数据库名称 + SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://root:oneapi@47.93.172.156:3306/JieNote" # 替换为实际的用户名、密码和数据库名称 SECRET_KEY: str = os.getenv("SECRET_KEY", "default_secret_key") # JWT密钥 ALGORITHM: str = "HS256" # JWT算法 ACCESS_TOKEN_EXPIRE_MINUTES: int = 5 # token过期时间 diff --git a/app/curd/article.py b/app/curd/article.py new file mode 100644 index 0000000..987a456 --- /dev/null +++ b/app/curd/article.py @@ -0,0 +1,100 @@ +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload +from sqlalchemy.future import select +from models.model import User, Group, Folder, Article, Note, Tag, user_group + +async def crud_upload_to_self_folder(name: str, folder_id: int, db: AsyncSession): + new_article = Article(name=name, folder_id=folder_id) + db.add(new_article) + await db.commit() + await db.refresh(new_article) + return new_article.id + +async def crud_get_self_folders(user_id: int, page_number: int, page_size: int, db: AsyncSession): + query = select(Folder).where(Folder.user_id == user_id, Folder.visible == True).order_by(Folder.id.desc()) + if page_number and page_size: + offset = (page_number - 1) * page_size + query = query.offset(offset).limit(page_size) + + result = await db.execute(query) + folders = result.scalars().all() + return folders + +async def crud_get_articles_in_folder(folder_id: int, page_number: int, page_size: int, db: AsyncSession): + query = select(Article).where(Article.folder_id == folder_id, Article.visible == True).order_by(Article.id.desc()) + if page_number and page_size: + offset = (page_number - 1) * page_size + query = query.offset(offset).limit(page_size) + + result = await db.execute(query) + articles = result.scalars().all() + return articles + +async def crud_self_create_folder(name: str, user_id: int, db: AsyncSession): + new_folder = Folder(name=name, user_id=user_id) + db.add(new_folder) + await db.commit() + await db.refresh(new_folder) + +async def crud_self_article_to_recycle_bin(article_id: int, user_id: int, db: AsyncSession): + # 1. 异步查询 article,并加载 folder 关系 + query = select(Article).options(selectinload(Article.folder)).where(Article.id == article_id) + result = await db.execute(query) + article = result.scalar_one_or_none() + if not article: + raise HTTPException(status_code=404, detail="Article Not Found") + + # 2. 检查是否是自己个人的article + if article.folder.user_id != user_id: + raise HTTPException(status_code=403, detail="This is not your own article") + + # 3. 修改 visible 字段 + article.visible = False + await db.commit() + await db.refresh(article) + +async def crud_self_folder_to_recycle_bin(folder_id: int, user_id: int, db: AsyncSession): + # 1. 异步查询 folder + query = select(Folder).where(Folder.id == folder_id) + result = await db.execute(query) + folder = result.scalar_one_or_none() + if not folder: + raise HTTPException(status_code=404, detail="Folder Not Found") + + # 2. 检查是否是自己个人的folder + if folder.user_id != user_id: + raise HTTPException(status_code=403, detail="This is not your own folder") + + # 3. 修改 visible 字段 + folder.visible = False + await db.commit() + await db.refresh(folder) + +async def crud_read_article(article_id: int, user_id: int, db: AsyncSession): + query = select(Article).where(Article.id == article_id) + result = await db.execute(query) + article = result.scalar_one_or_none() + if not article: + raise HTTPException(status_code=404, detail="Article not found") + folder_id = article.folder_id + + query = select(Folder).where(Folder.id == folder_id) + result = await db.execute(query) + folder = result.scalar_one_or_none() + if not folder: + raise HTTPException(status_code=404, detail="Folder not found") + uid = folder.user_id + gid = folder.group_id + + if uid: + if user_id != uid: + raise HTTPException(status_code=403, detail="This is an article of other user") + if gid: + query = select(user_group).where(user_group.c.user_id == user_id, user_group.c.group_id == gid) + result = await db.execute(query) + relation = result.first() + if not relation: + raise HTTPException(status_code=403, detail="This is an article of a group which you don't belong to") + + return article.name \ No newline at end of file diff --git a/app/models/model.py b/app/models/model.py index b40e8bc..0be24f0 100644 --- a/app/models/model.py +++ b/app/models/model.py @@ -44,6 +44,8 @@ class Folder(Base): create_time = Column(DateTime, default=func.now(), nullable=False) # 创建时间 update_time = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False) # 更新时间 + visible = Column(Boolean, default=True, nullable=False) # 是否可见 False表示在回收站中 + # 关系定义 user = relationship('User', back_populates='folders') group = relationship('Group', back_populates='folders') @@ -63,6 +65,8 @@ class Article(Base): folder_id = Column(Integer, ForeignKey('folders.id')) create_time = Column(DateTime, default=func.now(), nullable=False) # 创建时间 update_time = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False) # 更新时间 + + visible = Column(Boolean, default=True, nullable=False) # 是否可见 False表示在回收站中 folder = relationship('Folder', back_populates='articles') notes = relationship('Note', back_populates='article') diff --git a/app/routers/router.py b/app/routers/router.py index 44409d0..c086127 100644 --- a/app/routers/router.py +++ b/app/routers/router.py @@ -4,6 +4,7 @@ from app.api.v1.endpoints.note import router as note_router from app.api.v1.endpoints.user import router as user_router from app.api.v1.endpoints.aichat import router as aichat_router +from app.api.v1.endpoints.article import router as article_router def include_auth_router(app): app.include_router(auth_router, prefix="/public", tags=["auth"]) @@ -17,8 +18,12 @@ def include_user_router(app): def include_aichat_router(app): app.include_router(aichat_router, prefix="/chat", tags=["aichat"], dependencies=[Depends(get_current_user)]) +def include_article_router(app): + app.include_router(article_router, prefix="/article", tags=["article"], dependencies=[Depends(get_current_user)]) + def include_routers(app): include_auth_router(app) include_note_router(app) include_user_router(app) - include_aichat_router(app) \ No newline at end of file + include_aichat_router(app) + include_article_router(app) \ No newline at end of file diff --git a/app/schemas/article.py b/app/schemas/article.py new file mode 100644 index 0000000..22145a4 --- /dev/null +++ b/app/schemas/article.py @@ -0,0 +1 @@ +from pydantic import BaseModel diff --git a/requirements.txt b/requirements.txt index 78de9392721db096ca4bd0a862a0575abae5c2df..c98a539c31639c14e2579023f208364e7ce51500 100644 GIT binary patch literal 1380 zcmZ8h!A`+1ehrJ+O1@;Qwel+$;C!0egeftMNy$ZQVA+ z`*F)w!LvXla^#4c0e8Tikr|U*iSx{WDm{*@WE#{=sV(s2?x&2Ifjm>x#A-g=I`a2j z8#!x=cx#*z)ogGWmodqH9S||<)|QUj*$R0OIdmNH(q@iYDkyP81uL)?j_SkS=rPB8 z>Q>mNTj@mN*&EeD^q{526BzI9&0akpaj8Spiq=P*7kjpcI^(7*j1?!T6XqcE{osT| zeY8(qRJQe5P=C&SPp3{X;uHz{6Bv7#nr7cw*&J#}alMb5RpI@2EAM8?D`=R9ojjcd z?W8KcLlr4zk5jLWqC(VX80K5ET>{6s<=(JrA+CWFo4!9eVlMF|;Xk)912_8^_fTH3 t=C;oS*l%a2r#w_IzXM6DpL_cWeb7eqkxN^x1Gbiv!->%LGwPtVPyfFI#Pt9G literal 841 zcmXw1%Z}S16y5VLMg}{{%z{O?RU=I`mAZ@wCJBuV6mHV-e0|R)ljY?;I4>JgT`14t zXv`>k*$X@SrwF4T*PNxu|P&9fifG8yT~~A@z7% zR$kCj_2THtn$aEQfEDKpg;Ov__j)6h9F%Y;uJP>-+ini-YRn5-FT$lc6}wozHOj4M zm(sb|e8K&Zfom~^N(GQqgEYmOSGuH#9~$5(k&k!2@G4)0Uu>8UcRPp4QYt*8f$C%{ z*$fmQ0QO2hU%G71AzBK25KS$IQJjnSarWRe0HzWbrCJks#DyrkR6OJ9L4oTJ?rKmH zVTvGwtzP^ISC?(6{3ZGt^<*Ql-!Mtx^7G;FK#voQ=577{`&XM`Yv0!W z`u_7rGYjQI=nhKy8-