From 5632b0d69c9a070d66c727abdf05d6a7b3ac7738 Mon Sep 17 00:00:00 2001 From: jayhgq Date: Thu, 30 Apr 2026 16:01:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=9D=83=E9=99=90):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=9D=83=E9=99=90=E6=A0=91=E5=BD=A2=E7=BB=93=E6=9E=84=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在权限模型中添加 parentid 字段支持树形结构 - 新增权限树形结构 API 接口 - 更新初始化脚本以支持权限树形结构 - 调整权限字段长度限制 --- .../__pycache__/init_data.cpython-313.pyc | Bin 13046 -> 15778 bytes .../auth/__pycache__/models.cpython-313.pyc | Bin 3769 -> 3856 bytes .../auth/__pycache__/schemas.cpython-313.pyc | Bin 8746 -> 9508 bytes .../__pycache__/urls.cpython-313.pyc | Bin 8027 -> 9451 bytes backend/auth/apps/permissions/urls.py | 37 +++++ backend/auth/booksystem.db | Bin 49152 -> 0 bytes backend/auth/init_data.py | 146 +++++++++++++----- backend/auth/models.py | 5 +- backend/auth/schemas.py | 16 +- 9 files changed, 159 insertions(+), 45 deletions(-) delete mode 100644 backend/auth/booksystem.db diff --git a/backend/auth/__pycache__/init_data.cpython-313.pyc b/backend/auth/__pycache__/init_data.cpython-313.pyc index 501cd715062c661d1526e0716394d1cea8d9a565..ba6fff6f366f98d673fce413d7a7d353b828e385 100644 GIT binary patch delta 6772 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 delta 3933 zcma)8Z*UXG72lK2(n+#p*_MT6Tb5*71_dJ<8`-gp4PpYGSYRkuhbBN2)jAvz{V{tI z0h+03naP+bNlZ7ig#0PZz{KeX;(<(?CeumH6#A`m3IyzHnRZez@Xf>|Q~IIP-95=Z zFq8IX?%i+S+xK?gZ+G9``#<_tu*{pyMu4Bm&+f%dt;a3ZxpCY&0eHYOVKBfz#!av$ ztQpWkEyda}J5T~k26Rw2poe;@W5e8l0U9ta*~P5U@VXkXmxabcOHW&-LW`rV(n8BX zTjoN`ShI{cS_(}QHI|K(`pn1wV&@oHCkhE+`1seMTm_!y5&X_;SOA_kHUc-9HN$Io zEzkP3diKddvLQ=XN~>*%t_gUTB!(Z4;*P(w#0TCd@@k!ifB1zh#EH#H7Rxv;K1Nv{D!-w1fzv&K*5pV8TSbR74bq zdV~mN)B+LZqXJCEMG>#Onhtdz)hrcQprees<`mxrR@7aVcos-e52~U@>Aqx-6e23r zs9h;R&nWv#-RUvI(ygNQRnb%&c9Et*Sc=Xzj`%)g27L@92Lc}$jlWH1Ny@T;$Ot(( zA}%n%?LLfN(R*tRYe$5Xa9|Yn$&K+;JQG=@7Hw;?b62gJZ;uLU?SWMOpr<2KeMlKnJ@l;0ECWMsF1PvsV4GR0RaVW%8$&0cM z3Su^qk!^P2KwQkkQzL3KEJsP3YiGqGU1B7Yj*yY69?>aBqObtCOhje#LlGe!M748YA)W0*&?V2ukP1H&opUrd65vbDsPxybiJ*|u( z`PNEXqIvFzco4}Kl){lb_X5Rg{bTT)%UY+C*y_1v^rn4Pt}A@Sz4?o-@I>YV#~DYy zD=e{2$-Q|_2Wsjh);?WVFR_k)R{6#Z(>CYHJ$aiKBa=<43tTY;{!vp;Xq=X@EjXyvdA-b&FY}y!c4B|t+j(VoS?4FK zt_S&ike7D6AVpL89qBKF>1nT@+Q-dTtqs#HfyrdPsPx923+>MOPFQccHXP_p!0<<`yW z>!vN%H{wU*KYQsq=a4wZ-#GKQ@tqFIwdP7~K&t7S+$&i^SGf(dCg-*09&D8?o33*G zH(7&Z^nb~=ejR3jrTxKI?#=hwfuZIehs6Uij5o!RP;37>W~!a(?_j4onlXOzqNA$6 zlfCEul7f8B)*)gsGOqup{{)Kf! zI7uk2C}ik)h4n<|A)*lrA4g9k)Y0+1=sjn5bvZGT9~zNVm*VNbXX8K4+L9nvZM;xl zA1bl7Vu*i34~iKf>C-?HN;d`=6Dfz4ungO9K_utPr-fGhL3K?vhDr>ejkGp4xtL=} zXjFrQAu7{0X6{rr~f620rs694)4(lpdO$3-?q4x(TAmQc`Hf zxc;C~ug&hoHrcXJ6v&k*!u9Bscl*i^HZ83Uq`#4tYo+f134UQ&^I@HyP%#MG(0eWC zYQKl^_u+P8u6#A8rLRNIJf6G0Oq z!#sWzZ1CxoWhFVnE-YkYG?t8~BC#k+`|F*uS*eealpw@}m`HX@VWIIlpY~T7pU0ZB z_!Z00zOKd#U-@Mn(}(^LIH()MTDTRh?R0%-s{1<+SCCZU53pF658}GITXvJU4-JVG zvV~Hc5yVV{{6@kNth@y-B)jT0OavY4c4&+Y>SOBBrS8vBkJgAj4qD51k^fx$2J!2| zZ#9|;zO+-P@JAPj+@+B0T1Zl&QSrtkSCGhH;f7>S%4Rw^zSJ;7nJA27ofn(MBnIRT zriEeERo0Kr_iW9XK#609t)F2VXISS9TlYXDi~Q4?4O-?MCe+QoTY=F>OsI#QBxp** zgnHR2EyYz#XdOFcr+5Xkfnle7<)lv!Lu4xjI_5IN|x%64&IBAOU}QAi&2<4e|$ zPONq2WL*;fRwP77N`l0Z3GV1gzJ$uiC37bskC zYjNakLg<5OtRp0eQ7q=P48z<2tv7)4@4#1;2fiDiWlq<_w4HqAKER*3R~QuOT~o#} PEhjgB1Mml?u-|_H#WliB diff --git a/backend/auth/__pycache__/models.cpython-313.pyc b/backend/auth/__pycache__/models.cpython-313.pyc index 9ab9849f2aaafcb976a7e91ada3573a08ca3d893..c064206ac73364c3030f067e8155b1f46cb641b9 100644 GIT binary patch delta 328 zcmdlfJ3)@`GcPX}0}vRne9q+H-N@&_#l*xoIe;tEkUf|;m@k;$lDUW{hE0hfhEs_l zSiqDKq?myrm?c;+mpO%w^1ED}wZ(v+U;#_hsrKe?ORP@f5?zf=WCJnz`{Y;N<*nG-x+-tXx! zc(HKB^Ci1pF5LEf*Yam87CxOj{r#TKBI(IHxaB9`;Z|W3o6N{#$H`HUSd^Mql9@8u zjwhIL@#IN7%KE~SwO6oisJqDR*5GoJTX3S<42Vd|0l$mf!9X#7(Fy4b3NIK%T;z{z h@YsBn$Cr^YYO*q)D~~WE+XRs>3_$9m-sBQKR{+1TThIUi delta 241 zcmbOrw^NqyGcPX}0}zzY|CqU&XCt2j7t<$($pKuMikwOe!Mvu7AV~&>V3uIMU@lAM zBAysFC59N@7``C6$&O6I>ioe1Kv4mNC`3In2~#UC7%XJTQY0871T3K!(O?vvY|mpixt%A7amwUdJjx0}leHF< zZpgdH?b_gSlUs12+6;(D$N{^H+(8X4n{{}785tudSMj;>2r;rv5c$FYq(16QKF#L} E0Ckx-TL1t6 diff --git a/backend/auth/__pycache__/schemas.cpython-313.pyc b/backend/auth/__pycache__/schemas.cpython-313.pyc index d8aee0c718c5342618b15096b46009fdc672b85c..61b0b9ee8f935a143d9ac22c43d5e080063a1a98 100644 GIT binary patch delta 1169 zcmZvaUr19?9LMj`yE*6n_-}5`ZMx>4hNKcC7}-OLSc$H#FUEZ^=Fma;=UfLC<+NuAe zpO&Nb4H;A`=7<_WUj;Ry3icTUqmV188#1X*)QedvjBA8E%nSL{&}(oPu(skFRS5)k zS+Lj|+||K!!&H1c$$JNSd^7{XfpS19{8mYyZr)xF*KN)%q~@2l=H8~3 zo^LG7tv{aGUY?{bC`@VwFH&Fsu)n9BI$*u8^Tzb_=7&X^2T@u9a2shQ&Z{`)SDV@A zLZfL2*A#ICA(yIIP-p45jBCUoyizCNHn+yxzUG=D-ne&hG?Cjf)*N=N@-1us2^AK{ zoGb2*M4@M_HSAsGJ?x^jk6d9NtsZ9pJ0q@OXAK~Pos+zV*2*b%uBe!GmPGBNungn_ zlwYyV?yfK0$Ll|ts@Y{3wyWe8yKXC2U4xQaEMYdY_qHzW2sS~CvNF4ej9@#m2>Ll$ zS?MwMvLrhwtClI`2r5uhCuV_Ky1#1bK~Ie9MT4l_)6)1STg!pcm^F&I`!vq$)w>Il zMIIl$?3X0}Kz}Fo`Of>K!GV6sw>1fe377!NJvM^z=z+V?t9WEoleQoR5Zws;`4mM8 z3sXy&8evP>g`x zs2VX2Nb;Bb(l8@-Q!t1z1YSqNlc6I@zZwc6P4No}L*3Y^aONssr}zO3Qm+`K{@(!D z!?nf^5hh zA`jRP`=mOAhq{4|)lT3P@qoQ>948a9jea?9lUWujy>GgMY5A^G@*c#9yfKVZ#4I~k z)|Q2{cDIGim-U%sugZp!Z!do$%xq4(_8wFbc%^Tw$@#QSO}a>=E6w4U&d(s(kGcPX}0}%Ag|ClKxGLcV$al%IRiHt1iDw?XBFEPr9Gv-Zh6jPimAZa2O z%x1}4#1+FG!_ErS5yKJ8s}7`t`HFbbxitAFS4rxz@&E-&C$E*1vML2JpUrK4Idj7E zJ=2~qXuQReoS%~FS0o2gf{=XH-~VF&h9W_rXpt(A(Bz$bkY8kS0T1`&w<7$E`IF0~ z3>cjz%Zo~gU1o8;DIhu_c|p>Kyo&;^4K5wZK%vPmq%s&QCI?CfC>DYY0TC5IT+9I^ z+8A!K$lPM#DAET@P3Gs7oP1k)B4h33Tp4M`(#f4NRg5i@_lc{pmID>EOxBex;3@$r z1rfEAm&*n)mVh;tDC9DBPR>^no%~&)gsTQ9dy6F}v$$mPMRrmCDv%Bk(Fw%GI+N{{ zr6zAulxC~~YkjWR&Nu<6b&6;sNS!u_XaW(nAfjaQLN*DmdJq#tOqgt??86RX`Duzz z?p4kZ0DB7TCa^X2letu6xmtk=7=gI>{$xXyMSQG`k&F`}zc7I4F9MSvs)$c!QB?o{ DwwQz+ diff --git a/backend/auth/apps/permissions/__pycache__/urls.cpython-313.pyc b/backend/auth/apps/permissions/__pycache__/urls.cpython-313.pyc index 308c4b6a7a8652e7cee923192533a8ce7fb151c7..8f9f371ccb2c8182ea4827b1604e1da72d642fb5 100644 GIT binary patch delta 3321 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
rq z`sz)r#2h_}TY7^P5q54xWP`A>g=~oRx1J=;e2H0}1}h>PxD}BHYrbqD52B8~L{GV? zmF-nxA`iF~mH`z{wvqu6M;j4KYw$z_ncEPt7gn|ud#Af6)*rUoDpth2bP;>@ii1k* z>#H}8eZu*^30Z1`6%k}^MZ{iM*-Gpaj`vKW-%=W^h}d&0BKB*(Y##eW>%{>y!xD*8 zE^39vzH-q#%-jl#eSPI7yd3YyMBI`aJP|?WHbm@&m94}+;h15G82kS}349`vU4c)? zL9&k!@-3-Fy6SsiPyW89mf}g>OOy_okJ<<;M)g?quUC&n&mO(|KH-+_99h-0kln%Xa6oEhglb5RV|H=F0Pvj5e zxAc7gx5&@Q>*O`ENY&Ut00ck)1V8`;KmY_l00ck)1V8`;{tpD$jD13w3}Nw=FxO=GgHjmg&Y|6PHcM0%;f1_B@e0w4eaAOHd&00JNY0w4ea z{{sRSQ?7v2zN@>#e{r8*P9~+YT`DhM|K{o|&xpzF?D!#jA(NlY**%lRd^nm2g@o8S zduqly9qBGjWg@Zo&PabK6xtb^K3}lXeG4b@kCh82M&_r+2Ok+ZymN4RBz`)zf3P%v z?)Z!~kR7+vhjY1}@!7LS4h=?!q7N@v*_qk1$7fEaTuo~)5_==jo;L~j1vU@>0T2KI5C8!X009sH0T2KI5CDO@fPhD7m)RhT z%cBH!K0+a$|3BvQ|L+0^;Q|PN00@8p2!H?xfB*=900@8p2!KG909y-C_x^u!O(Opy ze{eY=`c#=pmw5l*6^ZTfWZs;-al2RPy75pb@Rfb V@xtH63q|FHTj7PzMcd1|>py3sx+VYs diff --git a/backend/auth/init_data.py b/backend/auth/init_data.py index 6afc64b..ab64344 100644 --- a/backend/auth/init_data.py +++ b/backend/auth/init_data.py @@ -1,66 +1,129 @@ from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select +from sqlalchemy import select, text from models import Role, User, Permission from utils.password import get_password_hash +async def ensure_permission_table_structure(db: AsyncSession): + """确保 permission 表结构符合最新要求""" + print("检查并更新 permission 表结构...") + + # 检查表是否存在 + result = await db.execute( + text("SELECT table_name FROM information_schema.tables WHERE table_name = 'permission'") + ) + table_exists = result.scalar_one_or_none() + + if not table_exists: + print("permission 表不存在,将由 SQLAlchemy 创建") + return + + # 检查是否缺少 parentid 字段 + result = await db.execute( + text("SELECT column_name FROM information_schema.columns WHERE table_name = 'permission'") + ) + existing_columns = {row[0] for row in result.fetchall()} + + # 添加 parentid 字段 + if 'parentid' not in existing_columns: + try: + await db.execute(text("ALTER TABLE permission ADD COLUMN IF NOT EXISTS parentid INTEGER DEFAULT 0")) + print("添加 parentid 字段成功") + except Exception as e: + print(f"添加 parentid 字段失败: {e}") + + # 删除旧字段(如果存在) + for old_column in ['module', 'submodule', 'action']: + if old_column in existing_columns: + try: + await db.execute(text(f"ALTER TABLE permission DROP COLUMN IF EXISTS {old_column}")) + print(f"删除旧字段 {old_column} 成功") + except Exception as e: + print(f"删除旧字段 {old_column} 失败: {e}") + + await db.commit() + print("permission 表结构更新完成") + + async def init_permissions(db: AsyncSession): - """初始化权限数据""" - # 定义需要初始化的权限 - required_permissions = [ - {"name": "用户管理", "code": "user:manage", "description": "用户管理权限"}, - {"name": "用户查询", "code": "user:read", "description": "用户查询权限"}, - {"name": "用户创建", "code": "user:create", "description": "用户创建权限"}, - {"name": "用户更新", "code": "user:update", "description": "用户更新权限"}, - {"name": "用户删除", "code": "user:delete", "description": "用户删除权限"}, - {"name": "角色管理", "code": "role:manage", "description": "角色管理权限"}, - {"name": "角色查询", "code": "role:read", "description": "角色查询权限"}, - {"name": "角色创建", "code": "role:create", "description": "角色创建权限"}, - {"name": "角色更新", "code": "role:update", "description": "角色更新权限"}, - {"name": "角色删除", "code": "role:delete", "description": "角色删除权限"}, - {"name": "权限管理", "code": "permission:manage", "description": "权限管理权限"}, - {"name": "权限查询", "code": "permission:read", "description": "权限查询权限"}, - {"name": "权限创建", "code": "permission:create", "description": "权限创建权限"}, - {"name": "权限更新", "code": "permission:update", "description": "权限更新权限"}, - {"name": "权限删除", "code": "permission:delete", "description": "权限删除权限"}, + """初始化权限数据(树形结构)""" + # 定义需要初始化的权限(树形结构) + # parentid: 0 = 顶级模块, >0 = 子权限 + permission_tree = [ + # 顶级模块 + {"name": "权限管理", "code": "perm", "parentid": 0, "description": "权限管理模块"}, + + # 二级子模块 + {"name": "用户管理", "code": "perm:user", "parentid": 1, "description": "用户管理子模块"}, + {"name": "角色管理", "code": "perm:role", "parentid": 1, "description": "角色管理子模块"}, + {"name": "权限配置", "code": "perm:config", "parentid": 1, "description": "权限配置子模块"}, + + # 用户管理操作 + {"name": "查看列表", "code": "perm:user:list", "parentid": 2, "description": "查看用户列表"}, + {"name": "查看详情", "code": "perm:user:detail", "parentid": 2, "description": "查看用户详情"}, + {"name": "搜索", "code": "perm:user:search", "parentid": 2, "description": "搜索用户"}, + {"name": "创建", "code": "perm:user:create", "parentid": 2, "description": "创建新用户"}, + {"name": "编辑", "code": "perm:user:edit", "parentid": 2, "description": "编辑用户信息"}, + {"name": "删除", "code": "perm:user:delete", "parentid": 2, "description": "删除用户"}, + {"name": "导出", "code": "perm:user:export", "parentid": 2, "description": "导出用户数据"}, + + # 角色管理操作 + {"name": "查看列表", "code": "perm:role:list", "parentid": 3, "description": "查看角色列表"}, + {"name": "查看详情", "code": "perm:role:detail", "parentid": 3, "description": "查看角色详情"}, + {"name": "搜索", "code": "perm:role:search", "parentid": 3, "description": "搜索角色"}, + {"name": "创建", "code": "perm:role:create", "parentid": 3, "description": "创建新角色"}, + {"name": "编辑", "code": "perm:role:edit", "parentid": 3, "description": "编辑角色信息"}, + {"name": "删除", "code": "perm:role:delete", "parentid": 3, "description": "删除角色"}, + {"name": "导出", "code": "perm:role:export", "parentid": 3, "description": "导出角色数据"}, + {"name": "分配权限", "code": "perm:role:assign", "parentid": 3, "description": "为角色分配权限"}, + + # 权限配置操作 + {"name": "查看列表", "code": "perm:config:list", "parentid": 4, "description": "查看权限列表"}, + {"name": "查看详情", "code": "perm:config:detail", "parentid": 4, "description": "查看权限详情"}, + {"name": "搜索", "code": "perm:config:search", "parentid": 4, "description": "搜索权限"}, + {"name": "创建", "code": "perm:config:create", "parentid": 4, "description": "创建新权限"}, + {"name": "编辑", "code": "perm:config:edit", "parentid": 4, "description": "编辑权限信息"}, + {"name": "删除", "code": "perm:config:delete", "parentid": 4, "description": "删除权限"}, + {"name": "导出", "code": "perm:config:export", "parentid": 4, "description": "导岀权限数据"}, ] # 检查现有权限 result = await db.execute(select(Permission)) existing_permissions = result.scalars().all() - existing_permission_codes = {perm.code for perm in existing_permissions} + existing_permission_codes = {perm.code: perm for perm in existing_permissions} - # 检查需要创建或更新的权限 - permissions_to_add = [] - permissions_to_update = [] + # 创建或更新权限 + created_count = 0 + updated_count = 0 - for perm_data in required_permissions: + for perm_data in permission_tree: perm_code = perm_data["code"] if perm_code not in existing_permission_codes: # 权限不存在,需要创建 - permissions_to_add.append(Permission(**perm_data)) + db.add(Permission(**perm_data)) + created_count += 1 else: # 权限存在,检查是否需要更新 - existing_perm = next( - perm for perm in existing_permissions if perm.code == perm_code - ) - if existing_perm.name != perm_data["name"] or existing_perm.description != perm_data["description"]: + existing_perm = existing_permission_codes[perm_code] + update_needed = False + + if existing_perm.name != perm_data["name"]: existing_perm.name = perm_data["name"] + update_needed = True + if existing_perm.parentid != perm_data["parentid"]: + existing_perm.parentid = perm_data["parentid"] + update_needed = True + if existing_perm.description != perm_data["description"]: existing_perm.description = perm_data["description"] - permissions_to_update.append(existing_perm) + update_needed = True + + if update_needed: + updated_count += 1 - # 执行创建和更新操作 - if permissions_to_add: - for perm in permissions_to_add: - db.add(perm) + if created_count > 0 or updated_count > 0: await db.commit() - print(f"创建了 {len(permissions_to_add)} 个权限") - - if permissions_to_update: - await db.commit() - print(f"更新了 {len(permissions_to_update)} 个权限") - - if not permissions_to_add and not permissions_to_update: + print(f"创建了 {created_count} 个权限,更新了 {updated_count} 个权限") + else: print("权限数据已存在且无需更新,跳过初始化") print("权限数据初始化/更新完成") @@ -293,6 +356,7 @@ async def init_test_user(db: AsyncSession): async def init_all_data(db: AsyncSession): """初始化所有数据""" print("开始初始化数据库数据...") + await ensure_permission_table_structure(db) await init_roles(db) await init_permissions(db) await init_role_permissions(db) diff --git a/backend/auth/models.py b/backend/auth/models.py index 420e7e5..b428964 100644 --- a/backend/auth/models.py +++ b/backend/auth/models.py @@ -56,8 +56,9 @@ class Permission(Base): __tablename__ = "permission" id = Column(Integer, primary_key=True, index=True, comment="权限ID") - name = Column(String(50), nullable=False, comment="权限名称") - code = Column(String(50), nullable=False, unique=True, comment="权限编码") + name = Column(String(100), nullable=False, comment="权限名称") + code = Column(String(100), nullable=False, unique=True, comment="权限编码") + parentid = Column(Integer, default=0, comment="父权限ID(0表示顶级模块)") description = Column(String(255), nullable=True, comment="权限描述") createtime = Column(DateTime, default=datetime.utcnow, comment="创建时间") updatetime = Column( diff --git a/backend/auth/schemas.py b/backend/auth/schemas.py index 36e4dba..e535743 100644 --- a/backend/auth/schemas.py +++ b/backend/auth/schemas.py @@ -112,8 +112,9 @@ class RoleResponse(RoleBase): class PermissionBase(BaseModel): """权限基础模型""" - name: str = Field(..., max_length=50, description="权限名称") - code: str = Field(..., max_length=50, description="权限编码") + name: str = Field(..., max_length=100, description="权限名称") + code: str = Field(..., max_length=100, description="权限编码") + parentid: int = Field(0, description="父权限ID(0表示顶级模块)") description: Optional[str] = Field(None, max_length=255, description="权限描述") @@ -133,6 +134,17 @@ class PermissionResponse(PermissionBase): from_attributes = True +class PermissionTreeResponse(BaseModel): + """权限树响应模型""" + + id: int = Field(..., description="权限ID") + name: str = Field(..., description="权限名称") + code: str = Field(..., description="权限编码") + parentid: int = Field(..., description="父权限ID") + description: Optional[str] = Field(None, description="权限描述") + children: list = Field([], description="子权限列表") + + # 角色权限关联相关模型 class RolePermissionAssign(BaseModel): """角色权限分配模型"""