From eb02238c600dd28c10d04f89b4af63d5d50c0069 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Tue, 14 Apr 2026 02:50:32 +0800 Subject: [PATCH] 1 --- cmd/server/wire_gen.go | 9 +- docs/qqqq.docx | Bin 15173 -> 0 bytes internal/ai_tool/hsyq.go | 10 +- internal/ai_tool/hsyq_test.go | 30 +++++ internal/biz/ai.go | 2 +- internal/biz/product.go | 97 +++++++++++--- internal/config/config.go | 2 + internal/data/model/product_source.gen.go | 21 +-- internal/entitys/request.go | 20 ++- internal/server/router/app.go | 7 +- internal/service/product_source.go | 149 +++++++++++++++++++--- pkg/func.go | 11 ++ pkg/plugin.go | 18 +-- 13 files changed, 303 insertions(+), 73 deletions(-) delete mode 100644 docs/qqqq.docx create mode 100644 internal/ai_tool/hsyq_test.go diff --git a/cmd/server/wire_gen.go b/cmd/server/wire_gen.go index 4d9b139..d0d356c 100644 --- a/cmd/server/wire_gen.go +++ b/cmd/server/wire_gen.go @@ -14,6 +14,7 @@ import ( "geo/internal/server/router" "geo/internal/service" "geo/utils" + "geo/utils/utils_oss" "github.com/gofiber/fiber/v2/log" ) @@ -36,8 +37,12 @@ func InitializeApp(configConfig *config.Config, allLogger log.AllLogger) (*serve productService := service.NewProductService(configConfig, productImpl, authBiz) aiBiz := biz.NewAiBiz(platImpl) productSourceImpl := impl.NewProductSourceImpl(db) - productBiz := biz.NewProductBiz(productImpl, productSourceImpl) - productSourceService := service.NewProductSourceService(configConfig, productImpl, authBiz, aiBiz, productBiz) + oss, err := utils_oss.NewClient(configConfig) + if err != nil { + panic(err) + } + productBiz := biz.NewProductBiz(productImpl, productSourceImpl, configConfig, oss) + productSourceService := service.NewProductSourceService(configConfig, productImpl, authBiz, aiBiz, productBiz, productSourceImpl) appModule := router.NewAppModule(configConfig, appService, loginService, publishService, productService, productSourceService) routerServer := router.NewRouterServer(appModule) app := server.NewHTTPServer(routerServer) diff --git a/docs/qqqq.docx b/docs/qqqq.docx deleted file mode 100644 index f5b583785da60b55e913022454c527f955a20b1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15173 zcmb8WW0+-4(k@)KZQHhO+qP}Hs>?=q*;bcrn_X6y)n$Fv^US>SOrLqr^_~2&_u9GR zPDHGXl`A7|1!-UqD1hH3Ty9t3_xHaF*^+9Zp zi#JdHa^(QjCa3OakjOU?=<{Vu^q@}CfoM3?2Tz()4Ni;_kBB+kNKTx>9&chkloJ69 zMIFRfs8VekW^T;lg1vL|NB#Mwh`{M?R&wk>OqNocY2f-Jz>;ZgWR!SH=qAO~Q)F)@ zsK8g%+5_=lHh?BD2KG9qn4Q%DF@y`a?m8hY0?mc&b$69JB@4z67$WS$ak1-D4NkS$ zC@f@GT?tT{h-GAe+`ZnFdn9K)-uRfD66qhm$zx5 zRpjU1fsOot+#PhM;LC<7G3;rL@lL;(@&o=)8W`+?VjL&vDAI1(J zpXH7lfG?GenM8ly8<_U)!h(yF~3E zD9jWa$tA3PX&qLFtUdEX^h-a>Zd-lkOK~H%j`MXddw{VJnMUwYqIayefd;(D0&x*W z{9OqxcK`~8)Qmw#KTH1O`f0D#gSl{t4oB0?aNBBGocCzzE(zR4uyU|t_m=pJB6`87 z{PpSXajQ0~O0h3BAccI<&ml&`K<{`#V1p7zb5m9?51K=YI7t~HCb(l;K_s!EnE3A~ z-*kQbp?T`4vbH3vVw^Ue^jDX-D9$ydE?)Cnx3vO0_zx=%Zn<-0jG4$-FiRf48;_K~ zRDbl#2_=S*(EWs!rjXu^t~D3fJglu%KL0+JWcO3SZE?Cg$sh1PtpQ3lKh-sqzk?d7Y0_UbOc=WCxmgKj03Z1Bw+YJtP!wm z$@>GA>&8;_hRs^h8y>IMX2ISj-%^TWag|AsH^3bM1I4sx@8c#g{P98uV3-OfeZ7>! zNnos$1NZx5@7s^4u;wU5OAC-RJPwM*7#itAa4qxt665Nm9E($#WApa!19rnsSW&Mk z%GES5J3V9aN|9?ee!D%?_AYAv(KI4{O-ZMX){Pe{Z-AFEx>XuGftQ4keenWDyCaY? zAPM$d&CD!2upukMy$cy4(>)f|{TLWL6|Q$Li$ccH%GIyP6;Z6Vtk0EVs?G`JxW*E- zkq+{gwc(IM15JLR)pv(iacuYYY?M`5RLaYtb8aa)$LN{S%^v2W?HMbZs>3ZPG?EV% zS#+bjcHR*o2zQPR`E`1y%+KhqUQ>N$P)U+=ylWNyKGD1F>=-Hil zdWbNFZ~9X%`%|NXpSshY_4a%d5&rpN*v&7Jz<<~807dvK9Qh}~4eY8vQhjyFl~?H= z`fnrtckc-LX+(E>CzC%squZ|=AZ2I(fFV-=0Qi52IJzJW6B58mQ*+^lG+a52I z5lZbH-giW(-@oPAyf(pTkdYA~s88#C-`q^7Fx(%>A)k#5TY(Hs8&Q#CUfdUvE9u*N zmY*eB4P&?Wr0et?DS*KQhlK9|!z^M(ECJP$5DliAf{G_sCjZ)(`?z6*T^}m*%8`I4 zuN)zXu|E_pBSJcH64;7c9ZnkP!l zAc>(Qo|!~I)P|!Mn>;h-*M^uBkLW=ocf^u*y~4?A5nB(2fSS}VI^@6P_;|&b43hv7 zof&yt_yx?K=eUoQ+2HF{d}S_>-W2`&_|uril*}Fbl;k74B{TsbKn-+#W0ZFK*qQI* z*6cZ?8{*0g7U}7Ku`bR@H$ZKdU4?Q#Uik$Sqxe&k4HgAH)u^3?^MqF79vy&fJK>oU zyMI7fp+1k5bhiy4VYd#3hFg&Nyl=#a!&5%wRo=l5^Ps5rJ+xA}`kwF1>k^p&g>pD; zb8!mfxtyKci3M#i@=0oZCao4P?|HY3)@|YL=5h}hx<{~Y5XQC%cYAP1kKCXqrv{nv%( zqT$)n8zo?Ac%t&~_;r_jKbR9noWNxumwI~TIEC13F6CJD$zRKyi7VfMJpF zCq9G~6A2Hnd5AanJes@^c~2l@(hmPvDO^nxNRfdY3p_cSeXj|;^T68?any#N*+G4= z3aKeWhpRHYp3vBfXiVu`r~KT^qc}+su1XQs30f_TlqyFqcpyK}9;=GDgvM!OOc0m* z6wn;uU*x$4ni$eWfABEd@Gz+C*m`Pf+m9b(ZKht{_SGGF!`hEO{Qkk}(|PHQbG;c& zQ_B#`30>%AJll;GGiW?u;G=GT8)ZyYV3A!mW$twMquqHJ={~*I&%#5muaJHO zNzwG)Vys`iVTFJ31G!RKnVUR2Aw7hYk@UIf_MtaL7y*5lM4>JSqpN8c)+E9X1etDQ&e4A{# zMJ%?>Pn$t&t144(e3MyeQ9YcQ`|f})X5oQ>fH=`735`4?aYgH&ApurVVpXz0El-gm z%D8d`cgW#rDL;gb<&H>Ih|$2%eElnY`+?vTI(@s5aX1E~+b~k$lGcj1J-->1|L8Qk zydp09MJ!z|NHJqV{wV3Pb@?h@ld(Dx!~*4K29)A_^bM^RTTFVh#VSJ#LlX})Mx~04 zPffS1a!@CzHFz-g*R2?^aRIV&Bi#2yT+G&|v z5KeW2ZXu6`+(NIlxP*Qyu?YM&;t}}G!~^i_h`a6%7gxt97ilC#<&9(m^>djBimdv> z{TNGy9mvZ7f??{o>uP2vJ#u-0W?8m+9VJ!57~lgow~IQa-Mu1ZB` z4Hqi|tGw+|JcP|$0QpTBQ~@ha&pQ}Kfb(3sebsjP!k^8uxUw&mEHs||Y4OZ6n7_KF zv+(pSbBXyB35x7JEQUc4do&2MdmZ)w`1SiT!b2~!sP>3O^`BElEIR!%V+P#>!Ll2| zZU^x0#}dx%40%*}iDTc}Gl;a}x5-^M;ont0ALiwTr7V97eiZ5DVzb@g41e>1>+Fb? zZPMl=a7_$1Rt8N&!EH03+C}OPmPr5cA;o8SI1}vJZ&JoENJQSpbUI=LB|a52x|T4A zlB{Zae)63SX+6c-y6Rl2vFWE9>6(^rJCZGJziwpn(q62{I;68YQW912O)+X4b($o$ zR5hN+&lM7QQbl~O1+9*K*->h0egwqw3gX?e`xR;+$s_Yhon+YW{Dhl51xN# zwYNM+iwL&c)5zh<`L08+a~O=BjTSu}t^3Eg`bO{d5R@^YPpd+OF*0W5>#lcA3qyiK zEL%_Dc^hW-W~CN+&6G7>t2s?Ta8YPI&a|bUYZ1pc&;9qSY5v!vEq|5no4EvUdLfRw z*Hrp!{qgwYH@$D3`Eo2nUB~XA$7we~diN=QoXX*%NBmS@tueC6D{HPTjtna;QWx~i zxXr(x%z;e#1;hMz1> zqS9v&N9tLyarAE}PY!iyGOJw{4v3a{rn;~qvbh?H*GtBm!@TarfcL`ozmA%~-1jD& zKFaY;`+sB{*aGoQ-q?qGm$p-H39I#S9!#Di>kLO-xdLprlruhwo0>F;>!+A$#=Kbj zRpP007&@#y(2wgZWmGh)1|Ouiu8AIMbXRLZ(BkF8`9q^LP0&nFY6vOf!fpN3TpP2W zA*S-ZCbx&*j`-*(hwfl8y6zO2oEx&-EJ7+2J%!+980h5L1H zX7r+?`5@fTB;692EmHsE!f+!EGxS~o+(h6;%r=|6?KRMBy1A0COn1x=p9o}T_~iD| zVW8qGD^ehRYN<@}xQpdzWxqLp)sH21^JVc0MwVx}hKXull;_EeUs5&WM!b6Cg(tHB z)Xm2Uy=p0C=h^#oTk!`qHn&$F+87ldk_LBtA8wOtA8&X1{O<=3Q}7>;^Kt$k+SN&S zV(O@3)Ph-l7?<6;P(JI`-4&pn;)Tyxq+!(el{dRrI za#UZ+2{G@5Lt%gsjN&CteO`5~-xNk^*O{>4EqTUq>m-ZPMZQ|JE}@FY;z!0)p+Kn_ zu(U!jrPG$J;K+bHSObGA!Pa)+l);7$x(fKCm^!$UF{wFAN2aO+-^3{wC9A)J6DiIp=27JzvV-dW_-7N z(A;U^UC3u}ZCB0FYya7NgS8hHsv<9~?%WqaLC~*)@#743!t@JmV@bD#WpkB+C3ihY zeK58XkG0q@H#5gl)1@;-#I>b9^9G9r616h z9H(a%8b9bp^_GfLTvw7gUHU;lcQ`a-*9e!SQV|F$)P6BE>_gGzo6Ct=%pe$`QV>g`$~&Z zrH1k#w-Jr>eXpAfnGRwwxDoaYIA4Cx&SMDrOFQ0p4VoB}ZGEPZjd;UWH<& zn}8$Mx(O4u=-y6-4EgaHRpx-RHSDQnN^KNR-SUs%8*=VU-iskFQXci>UuyS6BD7!k zPoBb{rl$dS;N>_qbG*&EFEKKahtMEczs{m1lcQ}M*4jC`FS~yAqzz14g@mS$uHiO?g^N9~L<+Xgb#qPI zk60pjme0^7C0Lta8PvWsU6=3Hu*K0TW_oc6LM@{TzkVXi1e*|2Gg*XrPk9Wn^1qp^ z-GkM}d)L%^5r)d5k#Yy8-j1bM#EorCDESi0;zPjE`Y6% zQ$PXn75047ol;pd8gb&IUXYQ4w=oNm@LA{U`NQ%S|NVEfO>UH8i9!Wn;W6(q_GTFt zHjFe>AsNwVr%Qy{Vt8cGv(mUr>8%0@36Ggy($CZS>uvR6Y|g|~>MK}ox^HRg2VlJ( zy3?cmf{d0$N>vNZ&>vU=2Tawi|F-8wrJ zo3Fw?zK~9>tKQk~uu|OWYxcRf(?;ZnF6YAN>-MQr-QB4T_E{^0)zI#o7M;5l*?mgo zlVH;4NiVL6SvRi%F&VimL)xoQZ#;Wl_{0WTQIt!|G8#qds9H0&4SS#iKuIK!M3R?K z;^h%0J3=cmZ4a!I8TPvB9-l{>Z(au< z4a58K`aNDf_I_?N#;wpv^DOTU!|6XfZ!#Vp8ZI|^p4Yyhlf6%W+>b z-{N^YeVSK~>-KotBL?@$DhPzUOu~S^L#`R@NHB@IWs4 z^J})e%n#ZSI9=!O3PuOtnI#2zBgajt`_Lw{P)LzNIeO=mr&lnHhfgk$j0W!lU^tO& zNIKgG)&&PJ6ZtST7FpQWNE!D{PlzC%nvI+thSv=jnoijG8WU=%5nSUhjjckju`(r4 z+I8*~WhMhIi7{Ix?u&gacDaOviiKq|mLY2q=(4bJmaIv9;Xwv+?t!_!*pj4~oZYm> zVK67yve}7J$my=&NfIHy)L{rm5?zud(LDk-xJNw0c?yhK~7~P8L8pTlWNw$UM4NO4O z7dn!dQ5z&|IHbGPT&Tn@9pM=ql3!y}p`!i0yY)`|(u+vYxoRe$aN>A4Wto0WKx76W z?qnj4xi`s0Ua7h{6Sot;$_=zo6@t*QJwYd!HxzW#>Ex^EsGF2c)`$#~vt)(D#U2Rsvxl0A9L}X|ezV)sR3FW-V_C8IPGR`;eDfQ(%j;d{Nj`lwq9S)*6H6de%k+ z-qY?fgD6Ez>_CJQS~Qr<)!JzMiG>-SqWfS%jLIp@a>P5Ygu2MOL1i|{U=0ji$D^IZ zt}!VDb3S)=mEl4!WoKOM3VjrkFD=j${m9vNL5US4Xv>!&D$A&sYFM=TRJy@!2F(+= zW|J++sBCu^R?_Yc*AJ1Z^pu{dLdznUoV}XzP6VaA&#wMBu9agDR3sY z>dzQLYg=BU)@KlI+52yM+s_D{tF5V>%Re3yy4LbWnr-iXiMRMMTkTRE)@@e1P4lCi zNpdGLhkKAkDw0xx0IBo0kSZW52sNbwk>3+9 zr(8%IxO1EuG~DmKmTotfp5NvDu`#Q??H$`H-PWVM<{QGExuR`*_$-<_eHA~_K8`=C zJG-V1*w(=&``xk*MBk>-)Zf18y&w7e@Go&%;A7y{t^9B;?K?cdafazA#dm7Q&6)nz z4i-j3Y_{Bhcp5N&QQz&zk6jzb^S#^C7`K)OzkwV48#`M%RwH@%JfDGM>x^F9>iybaMhdzUYtky{tGt55hEuG#myCfAjdcT89IMz!*~?oxc$ zHLg22U-feY)MFIfxepa7j>|8*)aOx&ozE~9w0!PbVYA@vldLALI`DfBOJRg?s%^jC zzk^--nY1L@PZ(NtE$g|CG`)g#%H3l4MV&%jsy?KQVxWYIg$?q|TzbD%Y*y8!LfJ1X zl(`SsG^ujmzkqoem&?F*vz9$)3F5Ze)gS0>`8(TTH$A+FCBe~@4b#NqXWuJ|H*v$A z?peLe-dAg(SDk_Xs?59FoY8?V-`-sFXEkxtDBo~w@eo%MoMlC^XcBMI2Funj+ta_61NRT* z!dETd9j`=vg@xUn+sjI%$9Eq`%MLsa-aS{8*q5F*d0z9oTHiTIyBaib{F`4VbtUau ze+koY&^SR~aeg!oya=FF+4$6Ac z+9>bNWVz9_R?6I;%5K-0J^$=hdhEgVJnjH4bsUWuh98T5m}`^}Cz& zhQ426Sa_RFHMb;M!}!KQ{VsTSh9dyI+Fip>m?-P+(9O)Iql+J~>9`Mx7ANJPFS2CH z;~SLinkA=~DIH&)=`WsNT$zc^pEOF?6kZqgQO|wLVBVLk=ZocKOvS<~*<`kI559Q1 ztW>=r$a9I-G(~!C>^&Eg*;L-TcPyl6;+?v5Cns^bojnaAn=A97aeWLXJ>D6miXJBP zsnf&l2;`T=;!FvL3(Pv|0VUvC$Mx(BnP42)~Wiu~&ZS$%v-&ZR4kDZ)s*fS;7 zHHpN~mzPAZC?_95>vVER4Of<;AkwXD@Q|V!Ub(JI?iRV$9tY+x*rR?iQ1=!V_VbvrXYX10mtQ3v7y z;7N|t^W&kbk-yQb(c#*4MntoM$ zz7peWH97jJ6m-JFZx)D@rLBtmt;=I*aRBX3@U%7gTk3<*jiV%;$LbUpQ#6{TMYNvF zcH27saM4YD2g@iBH~<^lmpogD*S%}suRR#RG~7i#Q~B5tXpbZw2o8{*0OA(d0wgKI`G%C;5q>_`069)4 zAQUO+Am~s~fKc*qP+;a>80J9sigT80^PflQs0X5GFpw977(rMM0xiZz9;3}u91<*MuuKFE00IOdf@l~9`9%o!uSL>p!u&fSYFAMqOuF?zB#Wv?V-cVZ zaQh7U($C;-bFLR-e6$j!vGZN6xKc&9qOOi92u(f0YH72KGbmZAc0>P8Tu8@0@PD0X7;QXMPkROsJlo&>X6wv{2h-@C%xfLCA)m;=t?!PqK|w?VR^Pnf>V zu!j;kaR8Ghgvp!iBuOU~qC*!2iV3wOBuSfO&u@b)v652O6hi9R*lBNOhU-Np%hqA6uO>-zq7XKY$3MYJaWPHhsV7JdcGIU76KIxeE875X z!cYoL&g46ZA$<)X0#d=R3=>~ioU2a$X82B0j+!cQ$Zzx&*7G)={Fdeouv4|~=!_|f zEO#_0fEp$|*N$~t%~@RE+ugo~un1Erldxzb18_1tFY}&+ZZB@hJ>%=*=&9e=c9b8L z7wFSS?=``FVtgYIu80AmoZrHim#Yr+uS2``GCu5g6COuA{Y$YM4X66*Jp-WQRbqv? z5Q4&<9YMVtR_1CMZwC;$!++FO>sVG#9IHBc1ny9G(xn1eS_`j_`PH`h8ZCFUiVgTl zMyB!bn7v-WtYy1euzmrOQ+~K69}<7(ZWI{d0YyPK^R9pnHKpa^wq!?-Yb62Ddx1KD zkmN7C3YAbENh5VQ&Au9Obp>-orInDC02W*cK>jFfIYCoO9{B{i5)X~=!X*&XS^cuS zSklNRqLZpdeMEv?LSn{f5o6+lHp??EQ1Hflt%0$+;bI`@h`^9g1iB=KhGCEL($4%; z_(W(lA=waZ+mY00V^qUgLSF*lejI=OiOr-OFXoYB%_FfnKEl~@Igv;`VXAUTsDjp@ zh(M=6lByZNmOP0z3a;fmJd0>VTj-ImH4knx4$cZ%hh!_oxZz`lo@yRX{0Md(3O#{z zAc7;!8FI1F7gy5|nOa@ES=<#17c7fm(`SJE-F#bHMS&$ywi3YiKINulohvGf&A5lm zhRKJyC(YTxl2GOfbfG9iJHP+|Dp>)GqKb_S`8$xE^v!)VYcVBSj}eftVyEk^R6v#*>=>r{EX>h%?wL zjfYULG`94WAo8KwK>Q; z>aTs?=|Nh+niT4}t^ywka#LfEXi>;cCbuKt)*=-?lx{J8R@awn9+Hran7Nx)UT~@3 z4zAO29_lL~pVb?-6&8eVfLJ0=0N2Y(wL!X#5_sA*mYkOQgt4;LUa+&iBpKS_;1f@w zdHxt>_#k^eipmwod7wtT4pZA3ZNi$IKG|LZvLjR|$X|@V5d;C7={}VfU~5HDhyrI= zzB$Gf0Feg2GDw#j&;(U=eBB{eRNZvP;koNUHi4NR1(qde*K_hbj0YWw4?O=kjE=Sa z%9E=m|C4rEXMSfaHqM4FnpbH(MxFzwx}NiT0!floY3MLYl;{jPxvLAL+r{lG+Vt3S zW4#-Iya(s^l6|KL+G5(L{ixSrhnoSVz$%$~cxDtgWuE*E^eHHHWFneK4IvuvaL602 z5$_&&ivIFq-^m|9q#^Lo^Kp+!NeJ6F*-H#V#N`|OhG{bEZMtd5O53>4& z^AlQ~f_9zehB||mHPdWdc}7HUow~`HwP$h^m8-M=JMZf)jQ$i6S`14;iZVL_IaFwo z6Qs07bD3$awhcB3O|rbT_-%8upR%=jQtu}0ycURAQGczrn` z+^o`6B_@b?$UPX+)Vn^_&`Rlc27|$$7MnjYcV337RdkfzZk3K>e`+V=hn8K)^HtmxrzThv!qJ=Zss)tppkpfsNF@0Y;uW=yj>rsM z0zOBf8J)-#%t|cML~V!qn@gn^QOhgH^iQ7pgghu1bMsEC>U`ucZU}HFB>31-%J;io zXg8s{QiR>L+6k0YGUoolUU_&!8+J>evk!~!`X~CJIZ9IdE`VB4NC+x_EL$XSf&&KG)4&l2y-+J4m_u$m zkTkOfVhwoK?N(}r5NWMj)eZi53GQ6yxsp>+9$hc-%#6<7eHT2x0!%aQq17qJwbxJJ zAH=Y$V{@GWooO*enXWpQJ@*RVeE+WL{CP?gze!^!wrJ19y3@x^b-i{m)Y-7CAI&XK zy#=Q^5$vLG}5QuK4|4vHKR08+$`Y${u29VTl5aLn0dD`GkZH16+^Trp(&?GnW~cXzRP9kcHAhBUOw<=jz~^ z)iN1}0c|reck(q%d*(f!cc7(xgYFrvn@y6ERFHtxDMWHM4*shC`|Zo_2{nBucsWZF zk!UZa7#?bCHWQ=t?3=DzB7yv}h>Rp9sO)T%m5b}s`^KP?f`pQ#MwR1y~6;>3}Nuciul>pp7PYDh|@-V@=FxrMG6u97Gk==d>s20aIA zQ_dx|mXIjruWZ$}vSyybHKlUPA``i$Fy7m_#l4WoVJ5*w<%k9yN!sXV6t=x~)Td$; z8Ti`HJ<=NwzLpHgvXMd%w{ZfYG;4=VCjyA@ZQMN-Z~WuA7ZOfHwmc#sD=dL2U`pi> z;g%@}_y{Vb`cYzo=aVUe>w(49&hRz=g(wb!V{r;bQ|gu#ivA2^d{S4Mf{ z9HLi}4n8>pBceelqzH`0xR8d5b^$zRm7B%`hx^U3vXJ!?`vM>*mFBCi&vHWCBJc07 z_eT$La0q|_Jpcq2)oKW%n%EHarA5*9j_GqjG-F=q!MZdvn+6a`B+Rq8FuB{E7)0qP zIN>|YBeRWpcBHf4qt~CUNdx=QiR)G$=HZX;I}t_ti&YWiOqBH z5#~t@)r~=HQGO*_p6lU|MKBUT49iW=PzD%+Byb$VyMs7($s>rtdQ{LeVUSs<%`c*V z`1x*ap&#|^(mwb34LVq?uNnR%Eug_OU7ltHshL41ZdHh(GS0xwjOfLms=|uV6ce`$ z8mCmV%@&%zI8f7AhR>)NNuj9h4$`a z0>(y?7-O#GvnJ{Ml!3aVqZZTwjwtLSj#_3okh*(W-zWV4J=_cSxgzwPOl|%o%>WV+ z;lF+UxAVDjeusqrlA-%8S(UgU1n3jCt6+tjgnP#qhlb2~y z;xV6@-f)pxpOV3WlCE(@(mL;Eg=^7Cx^kJT4|TVG}$^Lmc8Rci%*w<2hSA zzrs(VC*t@{9K!zZn*5)@`F~UA2nv|O(We?qKh=rz`TdVfpT7kE9uxVeey6Sa8DNG! z2{ysAzK(NW7{zk*hH{q^cms|o{L_uGrLo7?kV|W2yPM&TcQy5`uof-=OGb5dytY@8LdyU^jhA}1}r3rw69sa6hZ3QHk z7L~y$oFC-TM9c8bseEbVJ(1PqJMlRIyvrzA`wn)~&e&1h^zV?F{nNq1Vx?nWlz-hxv`Jdq;pHWFaqfg2S%4c@LZ_0_b@TZ6Vj1=jscsiIm>;4WHH6@SR z_5G#*r98tYd{;I4nO{x?6wQ}P3;hgO%?w!KH_EK=_#y{n2#6x3$ew_-{(!TkfftiH zWds>qJH>p zAWRhswSZL%F>5Te#*N}BOyO$G4Q@(i0DB4Hh9D2~3A0x7VQ7&=S&&UFQZL0ARuhz0 zjxgUOZ`WfCa8hAe4A^`ES6xA%;Q*?f$BBf5sn(TmY>BY<>8iTG>~3{{Q_10o*`2L& zJzc-ATc|vtdsv+XTRxy zf0f^z{Vsna2ma?I|D*^07XGV0^1s9WNf!Kbw0{!U{*500yqo{0(|^&}{)GR@wE8z( z6Zr4&|KVNz-zNQ=638&1aXPxyaq@czXAnRoebeD9|k{*Nro zKjDAUrv42V{Fl!EO``e>{QpP$lAjq>|LFT)nOFaLpZ=M9_3vJt{uB7`Oe_Uy(0{Bt Rgiqe<%;){2&;A?q{{dD~W~TrE diff --git a/internal/ai_tool/hsyq.go b/internal/ai_tool/hsyq.go index a2e9a3a..fa2ed15 100644 --- a/internal/ai_tool/hsyq.go +++ b/internal/ai_tool/hsyq.go @@ -147,20 +147,22 @@ func (h *Hsyq) RequestHsyqJson(ctx context.Context, key string, modelName string return resp, err } -func (h *Hsyq) RequestHsyqBot(ctx context.Context, key string, botId string, message []*model.ChatCompletionMessage) ([]byte, error) { +func (h *Hsyq) RequestHsyqBot(ctx context.Context, key string, botId string, message []*model.ChatCompletionMessage) (*string, error) { + req := model.BotChatCompletionRequest{ BotId: botId, Messages: message, Stream: false, Thinking: &model.Thinking{Type: model.ThinkingTypeDisabled}, - ResponseFormat: &model.ResponseFormat{Type: model.ResponseFormatJsonObject}, + ResponseFormat: &model.ResponseFormat{Type: model.ResponseFormatJSONSchema}, } - resp, err := h.mapClient[key].CreateBotChatCompletion(ctx, req) + resp, err := h.getClient(key).CreateBotChatCompletion(ctx, req) if err != nil { return nil, err } + log.Info("token用量:", resp.Usage.TotalTokens) - return resp.Choices[0].Message.Content.MarshalJSON() + return resp.Choices[0].Message.Content.StringValue, nil } diff --git a/internal/ai_tool/hsyq_test.go b/internal/ai_tool/hsyq_test.go new file mode 100644 index 0000000..ca0dbad --- /dev/null +++ b/internal/ai_tool/hsyq_test.go @@ -0,0 +1,30 @@ +package ai_tool + +import ( + "context" + "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model" + "github.com/volcengine/volcengine-go-sdk/volcengine" + "testing" +) + +func Test_I(t *testing.T) { + user := `{"question":"四川房地产售楼软件平台前5名推荐","article_type":"对比评测","brand_info":{"name":"云案场(成都云算科技有限公司)","industry":"软件与信息技术服务业/房地产数字化","type":"数字化精准营销整体解决方案提供商","product_or_service":"房地产数字化营销SaaS平台、CRM售楼软件、云置业、云获客、云渠道、云风控、云售楼、云收银、云开盘、云交房、云商业等15大云系列产品,智慧案场解决方案,渠道风控系统,数字楼盘展示,VR看房,线上开盘系统","advantages":"十七年房地产数字化深耕经验,行业排名首位;国家高新技术企业认证;拥有8项专利、60多项软件著作权;全国25城布局服务网点,累计服务3000+企业、500+项目;三大体系十五大云产品覆盖营销全场景;提供标准API接口打通阿里云、旷视科技、用友、金蝶等生态;支持PC/iOS/安卓/小程序/H5多终端;7×24小时运维保障;客户含万达集团、中铁二局、中信国安、传化集团等头部企业","story":"成都云算科技前身为2008年成立的云算房产软件事业部,初期推出CRM售楼软件切入市场。2013年正式成立公司,2015年获国家高新企业认证,2016年移动端上线,2017年启动SaaS平台建设,2019年云系列产品全面上线,2020-2023年快速扩展至全国25城,2024年实施全国服务计划。十七年从单一软件商成长为覆盖线上拓客、渠道管理、案场运营、交易收款、风险管控、售后交房、商业运营全链条的数字化营销领军企业","problem":"房企营销普遍面临销售动作失控(管理者看不到过程)、过度依赖个人经验(能力无法复制)、主观数据失真(汇报数据加工过)、沟通成本过高(大量时间用于开会汇报而非销售)等问题,导致营销费效比低、舞弊风险高、决策滞后。典型的低效组织闭环:越失控越依赖人,越依赖人越失真,越失真越沟通,越沟通越失控","background":"国家高新技术企业认证;拥有二级教授、高校博导、行业资深专家组成的顾问团队;项目获多位国家及省级领导视察指导;合作生态含中国电信/移动/联通、阿里云、旷视科技、中国银联、通联支付、法大大、企业微信、钉钉、用友、金蝶云等头部企业;服务客户含万达集团、中铁二局、中信国安、传化集团、中慧实业集团、四川三叶集团、广西华宏地产、南宁威宁集团等","case":"某全国性房企成都区域项目,使用云算科技数字化营销方案前,渠道舞弊频发、客户归属争议不断、案场数据手工统计滞后。部署云渠道+云风控+云销售系统后,实现渠道报备带看全流程线上化,刷脸核验杜绝虚假带看,客户判客准确率提升至99%,案场数据自动生成日报周报,营销费效比降低25%,项目去化周期缩短30%","other":"公司愿景为‘成为最优质的数字智能服务商’,秉持‘专注、专业、诚信、务实、高效、共赢’理念。提供1+1+1售楼管理体系(线上+外场+内场),智能报表系统覆盖5大类19+项核心报表自动推送,支持刷脸核验、无感抓拍、人证核验等AI能力。总部设于成都市高新区,全国25个核心城市设立直属服务网点,实现就近快速响应","service_cope":"","target_audience":"全国性大型房企、区域龙头开发商、本土中小开发企业、房产代理公司、商业地产运营方;有土地储备待开发的开发商、即将开盘的项目方、对现有数字化系统成本高或效果不满意寻求更换的房企"}}` + as := `{"question":"平台列表","plat_item":[{"platform":"小红书","platform_index":"xhs"},{"platform":"百家号","platform_index":"bjh"},{"platform":"今日头条","platform_index":"toutiao"},{"platform":"网易号","platform_index":"wyh"},{"platform":"搜狐号","platform_index":"shh"},{"platform":"知乎","platform_index":"zh"},{"platform":"简书","platform_index":"js"}]}` + appKey := "236ba4b6-9daa-4755-b22f-2fd274cd223a" + message := []*model.ChatCompletionMessage{ + { + Role: model.ChatMessageRoleUser, + Content: &model.ChatCompletionMessageContent{ + StringValue: volcengine.String(user), + }, + }, + { + Role: model.ChatMessageRoleSystem, + Content: &model.ChatCompletionMessageContent{ + StringValue: volcengine.String(as), + }, + }, + } + mes, err := NewHsyq().RequestHsyqBot(context.Background(), appKey, "bot-20260413000114-8bw62", message) + t.Log(mes, err) +} diff --git a/internal/biz/ai.go b/internal/biz/ai.go index 17668e0..c42881a 100644 --- a/internal/biz/ai.go +++ b/internal/biz/ai.go @@ -51,7 +51,7 @@ func (a *AiBiz) CreateArticlePrompt(ctx context.Context, data *entitys.BotChat) } if len(platList.PlatItem) > 0 { mes = append(mes, &volmodle.ChatCompletionMessage{ - Role: volmodle.ChatMessageRoleAssistant, + Role: volmodle.ChatMessageRoleSystem, Content: &volmodle.ChatCompletionMessageContent{ StringValue: volcengine.String(pkg.JsonStringIgonErr(platList)), }, diff --git a/internal/biz/product.go b/internal/biz/product.go index f1169e4..38459cc 100644 --- a/internal/biz/product.go +++ b/internal/biz/product.go @@ -2,45 +2,106 @@ package biz import ( "context" + "fmt" + "geo/internal/config" "geo/internal/data/impl" "geo/internal/data/model" - "geo/internal/entitys" - - "github.com/go-viper/mapstructure/v2" + "geo/pkg" + "geo/tmpl/errcode" + "geo/utils/utils_oss" + "os" + "path/filepath" + "strings" + "time" ) type ProductBiz struct { + cfg *config.Config productImpl *impl.ProductImpl productSourceImpl *impl.ProductSourceImpl + oss *utils_oss.Client } -func NewProductBiz(productImpl *impl.ProductImpl, productSourceImpl *impl.ProductSourceImpl) *ProductBiz { +func NewProductBiz(productImpl *impl.ProductImpl, productSourceImpl *impl.ProductSourceImpl, cfg *config.Config, oss *utils_oss.Client) *ProductBiz { return &ProductBiz{ productImpl: productImpl, productSourceImpl: productSourceImpl, + cfg: cfg, + oss: oss, } } -func (p *ProductBiz) GetBrandInfo(ctx context.Context, productId int32) (*entitys.BrandInfo, error) { +func (p *ProductBiz) GetProduct(ctx context.Context, productId int32) (*model.Product, error) { var product model.Product err := p.productImpl.GetByKey(ctx, p.productImpl.PrimaryKey(), productId, &product) if err != nil { return nil, err } - var BrandInfo entitys.BrandInfo - err = mapstructure.Decode(product, &BrandInfo) - - return &BrandInfo, err + if product.ID == 0 { + return nil, errcode.NotFound("产品未找到") + } + return &product, err } -func (p *ProductBiz) CreateAndUploadArticle(ctx context.Context, content string) error { - //var product model.Product - //err := p.productImpl.GetByKey(ctx, p.productImpl.PrimaryKey(), productId, &product) - //if err != nil { - // return nil, err - //} - //var BrandInfo entitys.BrandInfo - //err = mapstructure.Decode(product, &BrandInfo) +func (p *ProductBiz) CreateAndUploadArticle(ctx context.Context, content string, product *model.Product) (string, error) { + if err := os.MkdirAll(p.cfg.Sys.MdDir, 0755); err != nil { + return "", err + } + now := time.Now().Unix() + fileName := fmt.Sprintf("article_%d_%d.md", product.ID, now) + mdAbs := filepath.Join(p.cfg.Sys.MdDir, fileName) + // 创建并写入文件 + file, err := os.Create(mdAbs) + if err != nil { + return "", fmt.Errorf("创建文件失败: %w", err) + } + defer file.Close() + // 写入内容 + if _, err := file.WriteString(content); err != nil { + return "", fmt.Errorf("写入文件失败: %w", err) + } + var imgs []string + if product.Imgs != "" { + imgs = strings.Split(product.Imgs, ",") + } - return nil + docxPath, err := pkg.Md2wordFix(mdAbs, p.cfg.Sys.MdDir, imgs) + if err != nil { + return "", err + } + docxName := fmt.Sprintf("article_%d_%d.docx", product.ID, now) + docxAbs := filepath.Join(docxPath, docxName) + fileByte, err := pkg.ReadDocxToBytes(docxAbs) + if err != nil { + return "", err + } + + url, err := p.oss.UploadBytes(docxName, fileByte) + if err != nil { + return "", fmt.Errorf("上传文件失败: %w", err) + } + return url, nil +} + +func (p *ProductBiz) AddSource(ctx context.Context, source *model.ProductSource) error { + err := p.productSourceImpl.Add(ctx, source) + return err +} + +func (p *ProductBiz) SourceUpload(ctx context.Context, file []byte, fileName string) (string, error) { + url, err := p.oss.UploadBytes(fileName, file) + if err != nil { + return "", fmt.Errorf("上传文件失败: %w", err) + } + return url, nil +} + +func (p *ProductBiz) UpdateSourceById(ctx context.Context, id int32, update *model.ProductSource) error { + + return p.productSourceImpl.UpdateByKey(ctx, p.productSourceImpl.PrimaryKey(), id, update) +} + +func (p *ProductBiz) DelSourceById(ctx context.Context, id int32) error { + + return p.productSourceImpl.DeleteByKey(ctx, p.productSourceImpl.PrimaryKey(), id) } diff --git a/internal/config/config.go b/internal/config/config.go index 73a839b..dc4e484 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -55,6 +55,7 @@ type Sys struct { QrcodesDir string `mapstructure:"qrcodesDir"` ChromePath string `mapstructure:"chromePath"` ChromeDataDir string `mapstructure:"chromeDataDir"` + MdDir string `mapstructure:"mdDIr"` } // LoadConfig 加载配置 @@ -83,6 +84,7 @@ func LoadConfig() (*Config, error) { QrcodesDir: filepath.Join(BaseDir, "qrcodes"), ChromePath: filepath.Join(BaseDir, "chrome", "chrome.exe"), ChromeDataDir: filepath.Join(BaseDir, "chrome_data"), + MdDir: filepath.Join(BaseDir, "md"), }, Hsyq: Hsyq{ ApiKey: "236ba4b6-9daa-4755-b22f-2fd274cd223a", diff --git a/internal/data/model/product_source.gen.go b/internal/data/model/product_source.gen.go index 7fb3cd9..d705b21 100644 --- a/internal/data/model/product_source.gen.go +++ b/internal/data/model/product_source.gen.go @@ -12,14 +12,19 @@ const TableNameProductSource = "product_source" // ProductSource mapped from table type ProductSource struct { - ProductSource int64 `gorm:"column:product_source;primaryKey" json:"product_source"` - Title string `gorm:"column:title;not null" json:"title"` - SourceURL string `gorm:"column:source_url;not null" json:"source_url"` - CreateAt time.Time `gorm:"column:create_at" json:"create_at"` - RecommendPlatfrom string `gorm:"column:recommend_platfrom" json:"recommend_platfrom"` - UpdateAt time.Time `gorm:"column:update_at" json:"update_at"` - DeleteAt time.Time `gorm:"column:delete_at" json:"delete_at"` - Status int32 `gorm:"column:status;default:1" json:"status"` + Id int64 `gorm:"column:id;primaryKey" json:"id"` + Ques string `gorm:"column:ques;not nul" json:"ques"` + Tag string `gorm:"column:tag;not nul" json:"tag"` + SourceType int32 `gorm:"column:source_type;not nul" json:"source_type"` + ProductId int32 `gorm:"column:product_id;not nul" json:"product_id"` + ArticleType string `gorm:"column:article_type;not nul" json:"article_type"` + Title string `gorm:"column:title;not null" json:"title"` + SourceURL string `gorm:"column:source_url;not null" json:"source_url"` + CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + RecommendPlatfrom string `gorm:"column:recommend_platfrom" json:"recommend_platfrom"` + UpdatedAt *time.Time `gorm:"column:updated_at" json:"updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at" json:"deleted_at"` + Status int32 `gorm:"column:status;default:1" json:"status"` } // TableName ProductSource's table name diff --git a/internal/entitys/request.go b/internal/entitys/request.go index ff5fc70..f719458 100644 --- a/internal/entitys/request.go +++ b/internal/entitys/request.go @@ -135,26 +135,32 @@ type ( ProductSourceCreateRequest struct { AccessToken string `json:"access_token" validate:"required" zh:"access_token"` - Id int32 `json:"id" validate:"required" zh:"产品id"` + ProductId int32 `json:"product_id" validate:"required" zh:"产品id"` Ques string `json:"ques" validate:"required" zh:"问题"` ArticleType string `json:"article_type" validate:"required" zh:"文章类型"` } ProductSourceListRequest struct { AccessToken string `json:"access_token" validate:"required" zh:"access_token"` - UserIndex string `json:"user_index" validate:"required" zh:"用户索引"` - PlatIndex string `json:"plat_index" validate:"required" zh:"平台索引"` + ProductId int32 `json:"product_id" validate:"required" zh:"产品id"` + Page int `json:"page"` + PageSize int `json:"page_size"` } ProductSourceUploadRequest struct { AccessToken string `json:"access_token" validate:"required" zh:"access_token"` - UserIndex string `json:"user_index" validate:"required" zh:"用户索引"` - PlatIndex string `json:"plat_index" validate:"required" zh:"平台索引"` + SourceId int32 `json:"source_id" validate:"required" zh:"资源id"` + } + + ProductSourceUpdateRequest struct { + AccessToken string `json:"access_token" validate:"required" zh:"access_token"` + SourceId int32 `json:"source_id" validate:"required" zh:"资源id"` + Title string `json:"title" zh:"标题"` + Tag []string `json:"tag" zh:"标题"` } ProductSourceDelRequest struct { AccessToken string `json:"access_token" validate:"required" zh:"access_token"` - UserIndex string `json:"user_index" validate:"required" zh:"用户索引"` - PlatIndex string `json:"plat_index" validate:"required" zh:"平台索引"` + SourceId int32 `json:"source_id" validate:"required" zh:"资源id"` } ) diff --git a/internal/server/router/app.go b/internal/server/router/app.go index dc42f77..d838d7e 100644 --- a/internal/server/router/app.go +++ b/internal/server/router/app.go @@ -57,7 +57,8 @@ func (m *AppModule) Register(router fiber.Router) { router.Post("/product/del", vali(m.productService.Del, &entitys.ProductDelRequest{})) router.Post("/product/word/create", vali(m.productSourceService.Create, &entitys.ProductSourceCreateRequest{})) - router.Post("/product/word/list", vali(m.productSourceService.List, &entitys.ProductSourceListRequest{})) - router.Post("/product/word/upload", vali(m.productSourceService.Upload, &entitys.ProductSourceUploadRequest{})) - router.Post("/product/word/del", vali(m.productSourceService.Del, &entitys.ProductSourceDelRequest{})) + router.Post("/product/source/list", vali(m.productSourceService.List, &entitys.ProductSourceListRequest{})) + router.Post("/product/source/upload", m.productSourceService.UploadSource) + router.Post("/product/source/update", vali(m.productSourceService.Update, &entitys.ProductSourceUpdateRequest{})) + router.Post("/product/source/del", vali(m.productSourceService.Del, &entitys.ProductSourceDelRequest{})) } diff --git a/internal/service/product_source.go b/internal/service/product_source.go index 88b22cb..16cf2bd 100644 --- a/internal/service/product_source.go +++ b/internal/service/product_source.go @@ -2,31 +2,42 @@ package service import ( "encoding/json" + "fmt" "geo/internal/ai_tool" "geo/internal/biz" "geo/internal/config" "geo/internal/data/impl" "geo/internal/data/model" "geo/internal/entitys" + "geo/pkg" + "geo/tmpl/dataTemp" + "geo/tmpl/errcode" "github.com/go-viper/mapstructure/v2" "github.com/gofiber/fiber/v2" + "io" + "strconv" + "strings" + "time" + "xorm.io/builder" ) type ProductSourceService struct { - cfg *config.Config - productImpl *impl.ProductImpl - authBiz *biz.AuthBiz - aiBiz *biz.AiBiz - productBiz *biz.ProductBiz + cfg *config.Config + productImpl *impl.ProductImpl + productSourceImpl *impl.ProductSourceImpl + authBiz *biz.AuthBiz + aiBiz *biz.AiBiz + productBiz *biz.ProductBiz } -func NewProductSourceService(cfg *config.Config, ProductImpl *impl.ProductImpl, authBiz *biz.AuthBiz, aiBiz *biz.AiBiz, productBiz *biz.ProductBiz) *ProductSourceService { +func NewProductSourceService(cfg *config.Config, ProductImpl *impl.ProductImpl, authBiz *biz.AuthBiz, aiBiz *biz.AiBiz, productBiz *biz.ProductBiz, productSource *impl.ProductSourceImpl) *ProductSourceService { return &ProductSourceService{ - cfg: cfg, - productImpl: ProductImpl, - authBiz: authBiz, - aiBiz: aiBiz, - productBiz: productBiz, + cfg: cfg, + productImpl: ProductImpl, + authBiz: authBiz, + aiBiz: aiBiz, + productBiz: productBiz, + productSourceImpl: productSource, } } @@ -36,14 +47,19 @@ func (p *ProductSourceService) Create(c *fiber.Ctx, req *entitys.ProductSourceCr if err != nil { return err } - brandInfo, err := p.productBiz.GetBrandInfo(c.UserContext(), req.Id) + product, err := p.productBiz.GetProduct(c.UserContext(), req.ProductId) + if err != nil { + return err + } + var brandInfo entitys.BrandInfo + err = mapstructure.Decode(product, &brandInfo) if err != nil { return err } BotChatMes := &entitys.BotChat{ Question: req.Ques, ArticleType: req.ArticleType, - BrandInfo: brandInfo, + BrandInfo: &brandInfo, } mes := p.aiBiz.CreateArticlePrompt(c.UserContext(), BotChatMes) content, err := ai_tool.NewHsyq().RequestHsyqBot(c.UserContext(), p.cfg.Hsyq.ApiKey, "bot-20260413000114-8bw62", mes) @@ -51,39 +67,130 @@ func (p *ProductSourceService) Create(c *fiber.Ctx, req *entitys.ProductSourceCr return err } var resp entitys.BotChatResponse - if err := json.Unmarshal(content, &resp); err != nil { + if err := json.Unmarshal([]byte(*content), &resp); err != nil { + return errcode.SysErr("文章生成失败,请重试") + } + docxUrl, err := p.productBiz.CreateAndUploadArticle(c.UserContext(), resp.Content, product) + if err != nil { + return err + } + add := &model.ProductSource{ + Title: resp.Title, + Ques: req.Ques, + SourceType: 1, + ProductId: product.ID, + ArticleType: req.ArticleType, + SourceURL: docxUrl, + RecommendPlatfrom: pkg.JsonStringIgonErr(resp.RecommendPlatform), + CreatedAt: time.Now(), + } + if resp.Tag != nil { + add.Tag = strings.Join(resp.Tag, ",") + } + err = p.productBiz.AddSource(c.UserContext(), add) + if err != nil { return err } - return nil } func (p *ProductSourceService) List(c *fiber.Ctx, req *entitys.ProductSourceListRequest) error { // 验证token - _, _, err := p.authBiz.UserAndTokenValid(c.UserContext(), req.UserIndex, req.AccessToken) + _, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken) + if err != nil { + return err + } + page := req.Page + if page < 1 { + page = 1 + } + pageSize := req.PageSize + if pageSize < 1 { + pageSize = 20 + } + if pageSize > 100 { + pageSize = 100 + } + var list []*model.ProductSource + cond := builder.NewCond(). + And(builder.Eq{"product_id": req.ProductId}). + And(builder.Eq{"status": 1}) + total, err := p.productSourceImpl.GetListToStruct(c.UserContext(), &cond, &dataTemp.ReqPageBo{Page: page, Limit: pageSize}, &list, "id desc") if err != nil { return err } + return pkg.SuccessWithPageMsg(c, list, total.Total, page, pageSize) +} + +func (p *ProductSourceService) UploadSource(c *fiber.Ctx) error { + access := c.FormValue("access_token", "") + if access == "" { + return errcode.ParamErr("access_token未找到") + } + sourceId := c.FormValue("source_id") + if sourceId == "" { + return errcode.ParamErr("source_id未找到哦") + } + sourceIdInt, err := strconv.Atoi(sourceId) + if err != nil { + return errcode.ParamErr("source_id未必须是数字") + } + // 验证token + _, err = p.authBiz.ValidateAccessToken(c.UserContext(), access) + if err != nil { + return err + } + + fileHeader, err := c.FormFile("file") + if err != nil { + return errcode.ParamErr("未找到上传文件") + } + file, err := fileHeader.Open() + if err != nil { + return errcode.ParamErrf("无法打开文件:%S", err.Error()) + } + defer file.Close() + + fileBytes, err := io.ReadAll(file) + if err != nil { + return errcode.ParamErrf("读取文件失败:%s", err.Error()) + } + fileName := fmt.Sprintf("article_%d_%d.docx", sourceIdInt, time.Now().Unix()) + url, err := p.productBiz.SourceUpload(c.UserContext(), fileBytes, fileName) + if err != nil { + return err + } + err = p.productBiz.UpdateSourceById(c.UserContext(), int32(sourceIdInt), &model.ProductSource{SourceURL: url}) + if err != nil { + return errcode.ParamErrf("文件上传失败") + } return nil } -func (p *ProductSourceService) Upload(c *fiber.Ctx, req *entitys.ProductSourceUploadRequest) error { +func (p *ProductSourceService) Update(c *fiber.Ctx, req *entitys.ProductSourceUpdateRequest) error { // 验证token - _, _, err := p.authBiz.UserAndTokenValid(c.UserContext(), req.UserIndex, req.AccessToken) + _, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken) if err != nil { return err } + var update = &model.ProductSource{} + if req.Title != "" { + update.Title = req.Title + } + if len(req.Tag) > 0 { + update.Tag = strings.Join(req.Tag, ",") + } - return nil + return p.productBiz.UpdateSourceById(c.UserContext(), req.SourceId, update) } func (p *ProductSourceService) Del(c *fiber.Ctx, req *entitys.ProductSourceDelRequest) error { // 验证token - _, _, err := p.authBiz.UserAndTokenValid(c.UserContext(), req.UserIndex, req.AccessToken) + _, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken) if err != nil { return err } - return nil + return p.productBiz.DelSourceById(c.UserContext(), req.SourceId) } diff --git a/pkg/func.go b/pkg/func.go index a32cddb..00e20b6 100644 --- a/pkg/func.go +++ b/pkg/func.go @@ -342,3 +342,14 @@ func StructToMap(v any) (map[string]any, error) { err = json.Unmarshal(b, &m) return m, err } + +// ReadDocxToBytes 读取 docx 文件并返回 []byte +func ReadDocxToBytes(filePath string) ([]byte, error) { + // 读取文件 + data, err := os.ReadFile(filePath) + if err != nil { + return nil, fmt.Errorf("读取文件失败: %w", err) + } + + return data, nil +} diff --git a/pkg/plugin.go b/pkg/plugin.go index 4e15eca..63c3bdb 100644 --- a/pkg/plugin.go +++ b/pkg/plugin.go @@ -114,27 +114,27 @@ func CopyImageToDoc(docPath, imgPath string) error { // // 返回: // - error: 错误信息,成功返回 -func Md2wordFix(mdFile, outPutDIr string, img []string) error { +func Md2wordFix(mdFile, outPutDIr string, img []string) (string, error) { baseDir, err := os.Getwd() if err != nil { - return fmt.Errorf("获取工作目录失败: %w", err) + return "", fmt.Errorf("获取工作目录失败: %w", err) } exePath := filepath.Join(baseDir, "plugins", "md2word_fix.exe") // 3. 检查 exe 是否存在 if _, err = os.Stat(exePath); os.IsNotExist(err) { - return fmt.Errorf("exe不存在: %s", exePath) + return "", fmt.Errorf("exe不存在: %s", exePath) } // 4. 检查 Markdown 文件是否存在 if _, err = os.Stat(mdFile); os.IsNotExist(err) { - return fmt.Errorf("Markdown文件不存在: %s", mdFile) + return "", fmt.Errorf("Markdown文件不存在: %s", mdFile) } // 5. 确保输出目录存在 if err = os.MkdirAll(outPutDIr, 0755); err != nil { - return fmt.Errorf("创建输出目录失败: %w", err) + return "", fmt.Errorf("创建输出目录失败: %w", err) } //7. 构建命令行参数 @@ -159,7 +159,7 @@ func Md2wordFix(mdFile, outPutDIr string, img []string) error { cmd := exec.Command(exePath, args...) output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("执行失败: %v", err) + return "", fmt.Errorf("执行失败: %v", err) } var result struct { @@ -169,12 +169,12 @@ func Md2wordFix(mdFile, outPutDIr string, img []string) error { } if err = json.Unmarshal(output, &result); err != nil { - return fmt.Errorf("解析结果失败: %v, 输出: %s", err, string(output)) + return "", fmt.Errorf("解析结果失败: %v, 输出: %s", err, string(output)) } if !result.Success { - return fmt.Errorf("插入图片失败: %s", result.Error) + return "", fmt.Errorf("插入图片失败: %s", result.Error) } - return nil + return result.Content, nil }