miaoguoController.js 256 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144
  1. import moment from 'moment';
  2. import _ from 'lodash';
  3. import axios from 'axios';
  4. import fs from 'fs';
  5. import { promises as fsPromises } from 'fs';
  6. import path from 'path';
  7. import COS from 'cos-nodejs-sdk-v5';
  8. import config from '../../config/index.js';
  9. import { globalCache } from '../../util/GlobalCache.js';
  10. import constantClass from '../../util/constant/index.js';
  11. import { stringUtils } from '../../util/stringClass.js';
  12. import miaoguo from '../../model/miaoguo.js';
  13. import commonModel from '../../model/commonModel.js';
  14. import hanzi from '../../model/hanzi.js';
  15. import { Decrypt, Encrypt } from '../../util/crypto/index.js';
  16. import * as payInfo from '../../model/payInfo.js';
  17. import * as wxusersall from '../../model/wxUsersAll.js';
  18. import WXBizDataCrypt from '../../util/WXBizDataCrypt.js';
  19. async function requestMiaoguoSession(param) {
  20. const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${config.wx.miaoguo_appid}&secret=${config.wx.miaoguo_appsecret}&js_code=${param.Code}&grant_type=authorization_code`;
  21. try {
  22. const res = await axios.get(url, {
  23. transformResponse: [(data) => data],
  24. });
  25. const htmlString = res.data;
  26. const json = JSON.parse(htmlString);
  27. if (json && json.openid) {
  28. param.OpenID = json.openid;
  29. param.sessionKey = json.session_key;
  30. if (json.unionid)
  31. param.UnionID = json.unionid;
  32. return { errcode: 10000 };
  33. }
  34. return htmlString;
  35. } catch (err) {
  36. return { errcode: 101, errStr: err.message || String(err) };
  37. }
  38. }
  39. async function syncMiaoguoWXUsersAll(userInfo) {
  40. let obj = {
  41. UnionID: userInfo.UnionID,
  42. UserID: userInfo.UserID,
  43. ProductID: 166,
  44. };
  45. let wxUserList = await wxusersall.GetWXUsersAllInfo(obj);
  46. if (!wxUserList || wxUserList.length == 0) {
  47. obj = {
  48. UserID: userInfo.UserID,
  49. ProductID: 166,
  50. };
  51. wxUserList = await wxusersall.GetWXUsersAllInfo(obj);
  52. if (wxUserList && wxUserList.length > 0) {
  53. obj = {
  54. UnionID: userInfo.UnionID,
  55. ChangYiID: wxUserList[0].ChangYiID,
  56. };
  57. await wxusersall.UpdateWXUsersAll(obj);
  58. } else {
  59. obj = {
  60. UnionID: userInfo.UnionID,
  61. };
  62. wxUserList = await wxusersall.GetWXUsersAllInfo(obj);
  63. if (wxUserList && wxUserList.length > 0) {
  64. obj = {
  65. UserID166: userInfo.UserID,
  66. ChangYiID: wxUserList[0].ChangYiID,
  67. };
  68. await wxusersall.UpdateWXUsersAll(obj);
  69. } else {
  70. obj = {
  71. UnionID: userInfo.UnionID,
  72. UserID166: userInfo.UserID,
  73. };
  74. await wxusersall.AddWXUsersAll(obj);
  75. }
  76. }
  77. }
  78. }
  79. export async function MiaoguoLogin(ctx) {
  80. let param = ctx.request.body || {};
  81. if (param.Introducer) {
  82. if (param.Introducer.toString().indexOf("UserID") >= 0)
  83. param.Introducer = Number(param.Introducer.replace("UserID=", ""));
  84. }
  85. if (param.param) {
  86. const paramStr = Decrypt(param.param, config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
  87. param = JSON.parse(paramStr);
  88. }
  89. let result = await requestMiaoguoSession(param);
  90. if (result.errcode == 10000) {
  91. delete param.Code;
  92. if (param.sessionKey && param.iv && param.encryptedData) {
  93. const pc = new WXBizDataCrypt(config.wx.miaoguo_appid, param.sessionKey);
  94. const dataUnionID = pc.decryptData(param.encryptedData, param.iv);
  95. param.UnionID = dataUnionID.unionId;
  96. }
  97. delete param.sessionKey;
  98. delete param.iv;
  99. delete param.encryptedData;
  100. let currentUserID = 0;
  101. let userList = {};
  102. if (param.UserFamilyID && param.UserFamilyID > 0) {
  103. userList = await miaoguo.GetUsersInfo(param);
  104. if (userList && userList.length > 0)
  105. currentUserID = userList[0].UserID;
  106. const param2 = { UserID: param.UserFamilyID };
  107. userList = await miaoguo.GetUsersInfoByUserID(param2);
  108. param.OpenID = userList[0].OpenID;
  109. param.UnionID = userList[0].UnionID;
  110. } else {
  111. userList = await miaoguo.GetUsersInfo(param);
  112. if (userList && userList.length > 0) {
  113. currentUserID = userList[0].UserID;
  114. if (param.UnionID && userList[0].UserID && userList[0].OpenID && (!userList[0].UnionID || userList[0].UnionID == "")) {
  115. const paramUnionID = {};
  116. paramUnionID.OpenID = userList[0].OpenID;
  117. paramUnionID.UnionID = param.UnionID;
  118. await miaoguo.UpdateUsers(paramUnionID);
  119. userList[0].UnionID = param.UnionID;
  120. }
  121. }
  122. }
  123. if (userList && userList.length > 0) {
  124. let isLoseEfficary = 0;
  125. const time3 = moment().format('YYYYMMDD');
  126. const time4 = moment(userList[0].ProductServiceTime).format('YYYYMMDD');
  127. if (time3 > time4) {
  128. let obj = {};
  129. obj.ParentUserID = userList[0].UserID;
  130. await miaoguo.UpdateUserBinding(obj);
  131. obj = {};
  132. obj.UserID = userList[0].UserID;
  133. await miaoguo.DeleteMiaoguoFamily(obj);
  134. const param2 = {};
  135. param2.UserID = currentUserID;
  136. userList = await miaoguo.GetUsersInfoByUserID(param2);
  137. param.OpenID = userList[0].OpenID;
  138. param.UnionID = userList[0].UnionID;
  139. isLoseEfficary = 1;
  140. }
  141. delete param.UserFamilyID;
  142. param.LastLoginTime = new Date();
  143. if (!userList[0].IsMember && param.Introducer && userList[0].UserID != param.Introducer) {
  144. const param2 = {};
  145. param2.UserID = userList[0].UserID;
  146. const wechatUserInfo = await miaoguo.GetWechatServiceInfoByMGUserID(param2);
  147. if (wechatUserInfo && wechatUserInfo[0]) {
  148. if (!wechatUserInfo[0].IsProbation || wechatUserInfo[0].IsProbation == 0) {
  149. const obj2 = {};
  150. obj2.UserID = wechatUserInfo[0].WechatUserID;
  151. obj2.Introducer = param.Introducer;
  152. await miaoguo.UpdateWechatServiceUsersByUserID(obj2);
  153. } else {
  154. delete param.Introducer;
  155. }
  156. }
  157. } else {
  158. delete param.Introducer;
  159. }
  160. delete param.UserSource;
  161. delete param.SourceID;
  162. if (param.NickName == "陌生用户") {
  163. delete param.NickName;
  164. delete param.AvatarUrl;
  165. delete param.Language;
  166. delete param.Gender;
  167. delete param.City;
  168. delete param.Province;
  169. delete param.Country;
  170. }
  171. if (param.NickName == "微信用户")
  172. param.NickName = "陌生用户";
  173. if (!userList[0].ShowUserID || userList[0].ShowUserID == "")
  174. param.ShowUserID = stringUtils.Random(100, 999).toString() + userList[0].UserID.toString() + stringUtils.Random(100, 999).toString();
  175. if (ctx.query.IsPromotion) {
  176. if (!userList[0].PromotionLimitTime)
  177. param.PromotionLimitTime = userList[0].ProductServiceTime;
  178. }
  179. await miaoguo.UpdateUsers(param);
  180. userList = await miaoguo.GetUsersInfo(param);
  181. if ((!ctx.query.IsPromotion || userList[0].IsPromoter == 0) && userList && userList.length > 0) {
  182. delete userList[0].UserTree;
  183. delete userList[0].PromotionLimitTime;
  184. delete userList[0].WXAccount;
  185. delete userList[0].RealName;
  186. delete userList[0].IdentityCard;
  187. delete userList[0].Phone;
  188. } else {
  189. if (userList[0] && userList[0].PromotionLimitTime) {
  190. const a = moment(userList[0].PromotionLimitTime);
  191. const b = moment();
  192. const days = a.diff(b, 'days');
  193. userList[0].PromotionLimitDays = days;
  194. userList[0].PromotionLimitTime = moment(userList[0].PromotionLimitTime).format('YYYY.MM.DD');
  195. userList[0].WXServiceCharge = config.WXServiceCharge;
  196. userList[0].IndividualIncomeTax = config.IndividualIncomeTax;
  197. if (!userList[0].PayeeWechatAccount)
  198. userList[0].PayeeWechatAccount = userList[0].NickName;
  199. if (!userList[0].WXAccount)
  200. userList[0].WXAccount = userList[0].NickName;
  201. if (!userList[0].RealName)
  202. userList[0].RealName = "";
  203. if (userList[0].Introducer) {
  204. const param2 = {};
  205. param2.UserID = userList[0].Introducer;
  206. param2.FieldStr = "NickName,AvatarUrl";
  207. const userInfo2 = await miaoguo.GetUsersInfoByUserID(param2);
  208. if (userInfo2 && userInfo2[0]) {
  209. userList[0].IntroducerNickName = userInfo2[0].NickName;
  210. userList[0].IntroducerAvatarUrl = userInfo2[0].AvatarUrl;
  211. }
  212. }
  213. }
  214. }
  215. userList[0].ProductServiceTime = moment(userList[0].ProductServiceTime).format('YYYY/MM/DD 23:59:59');
  216. if (isLoseEfficary == 1) {
  217. userList[0].IsLoseEfficacy = 1;
  218. currentUserID = userList[0].UserID;
  219. }
  220. userList[0].IsNewUser = 0;
  221. } else {
  222. delete param.UserFamilyID;
  223. param.CreateTime = new Date();
  224. param.LastLoginTime = param.CreateTime;
  225. param.ProductServiceTime = param.CreateTime;
  226. param.PromotionDeliveryTimeIsUp = '2099-12-31 23:59:59';
  227. if (param.NickName == "微信用户")
  228. param.NickName = "陌生用户";
  229. await miaoguo.AddUsers(param);
  230. userList = await miaoguo.GetUsersInfo(param);
  231. userList[0].IsNewUser = 1;
  232. if (!userList[0].ShowUserID || userList[0].ShowUserID == "")
  233. userList[0].ShowUserID = stringUtils.Random(100, 999).toString() + userList[0].UserID.toString() + stringUtils.Random(100, 999).toString();
  234. const param3 = {};
  235. param3.UserID = userList[0].UserID || 0;
  236. param3.CardMaxNumberNew = 10;
  237. param3.CardMaxNumberHistory = 40;
  238. param3.BasicCardLimit = param3.CardMaxNumberNew + param3.CardMaxNumberHistory;
  239. param3.CardMaxNumberNewUrgent = 10;
  240. param3.CardMaxNumberHistoryUrgent = 40;
  241. param3.CardType = 0;
  242. param3.SortTypeIndex = 0;
  243. param3.MemoryLevel = 0;
  244. param3.ClickType = 0;
  245. param3.SecondConfigArray = "0,0,0";
  246. param3.ColorIndexArr = "1,2,3,4";
  247. await miaoguo.AddUserConfig(param3);
  248. }
  249. if (userList[0].ParentUserID > 0) {
  250. const userid = userList[0].UserID;
  251. const obj = {};
  252. obj.UserID = userList[0].ParentUserID;
  253. userList = await miaoguo.GetUsersInfoByUserID(obj);
  254. userList[0].ChildUserID = userid;
  255. }
  256. userList[0].Subscribe = 0;
  257. if (userList[0].UnionID) {
  258. await syncMiaoguoWXUsersAll(userList[0]);
  259. const obj2 = {
  260. UnionID: userList[0].UnionID,
  261. };
  262. const wccUserList = await miaoguo.GetWechatServiceUsersInfoByUnionID(obj2);
  263. if (wccUserList && wccUserList[0]) {
  264. userList[0].Subscribe = wccUserList[0].Subscribe;
  265. userList[0].IsProbation = wccUserList[0].IsProbation;
  266. if (userList[0].IsProbation == 0 && userList[0].IsPay == 1) {
  267. userList[0].IsProbation = 1;
  268. } else if (userList[0].IsProbation == 0 && userList[0].UserID < 1000) {
  269. const time4 = moment(userList[0].ProductServiceTime).format('YYYYMMDD');
  270. if (time4 > '20190801') {
  271. userList[0].IsProbation = 1;
  272. }
  273. }
  274. if (wccUserList[0].TempClassID > 0 || wccUserList[0].TempIsMember > 0) {
  275. const obj3 = {};
  276. obj3.UserID = userList[0].UserID;
  277. if (wccUserList[0].TempClassID && wccUserList[0].TempClassID > 0)
  278. obj3.ClassID = wccUserList[0].TempClassID;
  279. if (wccUserList[0].TempIsMember && wccUserList[0].TempIsMember > 0)
  280. obj3.IsMember = wccUserList[0].TempIsMember;
  281. await miaoguo.UpdateUsersByUserID(obj3);
  282. const obj4 = {};
  283. obj4.UserID = wccUserList[0].UserID;
  284. if (wccUserList[0].TempClassID && wccUserList[0].TempClassID > 0)
  285. obj4.TempClassID = 0;
  286. if (wccUserList[0].TempIsMember && wccUserList[0].TempIsMember > 0)
  287. obj4.TempIsMember = 0;
  288. await miaoguo.UpdateWechatServiceUsersByUserID(obj4);
  289. }
  290. }
  291. }
  292. delete userList[0].OpenID;
  293. delete userList[0].UnionID;
  294. const param2 = {};
  295. param2.ProgramID = 166;
  296. param2.Version = param.ProgramVersion;
  297. const result3 = await commonModel.GetProductVersionList(param2);
  298. if (result3 && result3[0]) {
  299. if ((param2.Version == result3[0].Version && result3[0].IsShowPay <= 0)
  300. || param2.Version > result3[0].Version) {
  301. userList[0].IsShow = result3[0].IsShowPay;
  302. } else {
  303. userList[0].IsShow = 1;
  304. }
  305. if (process.env.NODE_ENV != 'development') {
  306. const today = moment().format('YYYYMMDD');
  307. if ((userList[0].UserSource == '1001' && userList[0].System == "iOS 10.0.1")
  308. || (!userList[0].UserSource && (!userList[0].LastUserSource || userList[0].LastUserSource > 10000))
  309. || ((userList[0].NickName || "").indexOf("dgztest") >= 0)) {
  310. userList[0].IsShow = -1;
  311. } else if (userList[0].UserID == 28631 && today < "20230526") {
  312. userList[0].IsShow = -1;
  313. } else if (userList[0].LastUserSource == '1012'
  314. && userList[0].IsPay == 0
  315. && userList[0].IsMember == 0
  316. && userList[0].IsApply == 0
  317. && userList[0].IsTryOut == 0
  318. && userList[0].Model == "iPhone XS MAX China-exclusive<iPhone 11,6>") {
  319. userList[0].IsShow = -1;
  320. }
  321. }
  322. if (userList[0].IsMember === 1)
  323. userList[0].IsShow = 1;
  324. if (userList[0].IsShow == 1)
  325. userList[0].ShowText1 = "分享";
  326. else
  327. userList[0].ShowText1 = "";
  328. }
  329. if (userList[0].ClassID > 0) {
  330. const classList = await miaoguo.GetClassList(userList[0]);
  331. if (classList && classList[0]) {
  332. userList[0].ClassStartDate = moment(classList[0].StartDate).format('YYYY-MM-DD');
  333. userList[0].ClassEndDate = moment(classList[0].StartDate).add(classList[0].DayNumber - 1, "days").format('YYYY-MM-DD');
  334. userList[0].ClassDayNumber = moment(moment().format("YYYY-MM-DD 00:00:00")).diff(classList[0].StartDate + " 00:00:00", 'days');
  335. userList[0].ClassTotalDayNumber = classList[0].DayNumber;
  336. }
  337. }
  338. if (userList[0].IsShow <= 0 || userList[0].IsPay == 0) {
  339. userList[0].HasIntroducer = 1;
  340. } else {
  341. userList[0].HasIntroducer = 0;
  342. }
  343. if (userList[0].UserID == 0) {
  344. userList[0].IsMaintain = 1;
  345. }
  346. if (currentUserID > 0) {
  347. userList[0].CurrentUserID = currentUserID;
  348. userList[0].MainUserID = currentUserID;
  349. }
  350. result = { errcode: 10000, result: userList[0] };
  351. const log = {};
  352. log.UserID = userList[0].UserID;
  353. log.LogID = 374;
  354. log.Content = userList[0];
  355. log.Content = JSON.stringify(log.Content);
  356. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  357. }
  358. ctx.body = result;
  359. }
  360. export async function GetClassList(ctx) {
  361. const param = {
  362. ClassID: ctx.query.ClassID || 0,
  363. Sort: ctx.query.Sort || "desc",
  364. Flag: ctx.query.Flag,
  365. SqlType: ctx.query.SqlType,
  366. };
  367. const list = await miaoguo.GetClassList(param);
  368. for (const item of list) {
  369. item.EndDate = moment(item.StartDate).add(item.DayNumber, 'days').format('YYYY-MM-DD');
  370. }
  371. ctx.body = { errcode: 10000, result: list };
  372. }
  373. export async function GetMiaoguoCoupon(ctx) {
  374. const param = {
  375. UserID: ctx.query.UserID || 0,
  376. CouponType: ctx.query.CouponType || 0,
  377. };
  378. const list = await miaoguo.GetMiaoguoCoupon(param);
  379. if (param.CouponType == 0) {
  380. for (const item of list) {
  381. const arr = item.Name.split("#");
  382. item.Content = arr;
  383. delete item.Name;
  384. const today = moment().format("YYYY年MM月DD日");
  385. item.LimitTime = moment(item.LimitTime).format("YYYY年MM月DD日");
  386. if (today > item.LimitTime)
  387. item.IsLoseEfficacy = 1;
  388. }
  389. }
  390. ctx.body = { errcode: 10000, result: list };
  391. }
  392. export async function GetUserFolderList(ctx) {
  393. const param = {
  394. UserID: ctx.query.UserID || 0,
  395. OrderType: ctx.query.OrderType,
  396. IsNewData: ctx.query.IsNewData || 0,
  397. };
  398. const url = "GetUserFolderList?UserID=" + param.UserID;
  399. let result = globalCache.get(url);
  400. if (param.IsNewData == 1 || param.IsNewData == "1")
  401. result = 0;
  402. if (result === 0) {
  403. if (!param.OrderType) {
  404. const userConfig = await miaoguo.GetUserConfigData(param);
  405. if (userConfig && userConfig.length > 0)
  406. param.OrderType = userConfig[0].FolderOrder;
  407. param.OrderType = param.OrderType || 'UpdateTime desc';
  408. }
  409. const list = await miaoguo.GetUserFolderList(param);
  410. const arr = [];
  411. for (const item of list)
  412. arr.push(item.ID);
  413. param.FolderIDStr = arr.join(",");
  414. const list2 = await miaoguo.GetUserFolderCount(param);
  415. for (const item of list) {
  416. for (const countItem of list2) {
  417. if (item.ID == countItem.FolderID)
  418. item.Total = countItem.Total;
  419. }
  420. }
  421. result = list;
  422. if (param.OrderType == "CreateTime desc")
  423. result = _.orderBy(result, ['CreateTime'], ['desc']);
  424. else if (param.OrderType == "CreateTime asc")
  425. result = _.orderBy(result, ['CreateTime'], ['asc']);
  426. else if (param.OrderType == "UpdateTime desc")
  427. result = _.orderBy(result, ['UpdateTime'], ['desc']);
  428. else if (param.OrderType == "UpdateTime asc")
  429. result = _.orderBy(result, ['UpdateTime'], ['asc']);
  430. else if (param.OrderType == "Total desc")
  431. result = _.orderBy(result, ['Total'], ['desc']);
  432. else if (param.OrderType == "Total asc")
  433. result = _.orderBy(result, ['Total'], ['asc']);
  434. else if (param.OrderType == "Name desc")
  435. result = _.orderBy(result, ['Name'], ['desc']);
  436. else if (param.OrderType == "Name asc")
  437. result = _.orderBy(result, ['Name'], ['asc']);
  438. globalCache.set(url, result, config.BufferMemoryTimeLowNormal);
  439. }
  440. ctx.body = { errcode: 10000, result };
  441. }
  442. export async function GetUserFolderInfo(ctx) {
  443. const param = {
  444. UserID: ctx.query.UserID || 0,
  445. FolderID: ctx.query.FolderID || 0,
  446. };
  447. const url = "GetUserFolderInfo?FolderID=" + param.FolderID;
  448. let result = globalCache.get(url);
  449. if (result === 0) {
  450. const list = await miaoguo.GetUserFolderList(param);
  451. for (const item of list) {
  452. if (item.ID == param.FolderID)
  453. result = item;
  454. }
  455. globalCache.set(url, result, config.BufferMemoryTimeLowNormal);
  456. }
  457. ctx.body = { errcode: 10000, result };
  458. }
  459. function decryptWebID(value) {
  460. try {
  461. return Decrypt(String(value).replace(/@@@/g, '+'), config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
  462. } catch (e) {
  463. // web 端有时直接传原始 ID/PageID(如 0 或卡片ID)而非加密串,解密失败时回退为原值
  464. return String(value);
  465. }
  466. }
  467. function encryptWebID(value) {
  468. return Encrypt(String(value), config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
  469. }
  470. function decryptWebIDList(list) {
  471. if (!list)
  472. return [];
  473. const source = Array.isArray(list) ? list : String(list).split(',');
  474. return source
  475. .filter(item => item !== undefined && item !== null && item !== '')
  476. .map(item => Number(decryptWebID(item)));
  477. }
  478. function formatMiaoguoCardTime(card, isDeleteList = false) {
  479. if (card.CreateTime) {
  480. card.CreateTime = moment(card.CreateTime).format('YYYY-MM-DD HH:mm:ss');
  481. card.CreateTimeStr = stringUtils.GetDateTimeFormat(card.CreateTime);
  482. }
  483. if (card.UpdateTime) {
  484. card.UpdateTime = moment(card.UpdateTime).format('YYYY-MM-DD HH:mm:ss');
  485. card.UpdateTimeStr = stringUtils.GetDateTimeFormat(card.UpdateTime);
  486. }
  487. if (card.FirstTime)
  488. card.FirstTime = moment(card.FirstTime).format('YYYY-MM-DD HH:mm:ss');
  489. if (card.LastTime) {
  490. card.LastTimeStr = isDeleteList ? stringUtils.GetDateTimeFormat(card.LastTime) : moment(card.LastTime).format('YYYY-MM-DD');
  491. card.LastTime = moment(card.LastTime).format('YYYY-MM-DD HH:mm:ss');
  492. }
  493. if (card.LimitTime) {
  494. card.LimitTime = moment(card.LimitTime).format('YYYY-MM-DD HH:mm:ss');
  495. if (!isDeleteList)
  496. card.LimitTimeStr = moment(card.LimitTime).format('YYYY-MM-DD');
  497. }
  498. if (card.PigeonholeTime)
  499. card.PigeonholeTime = moment(card.PigeonholeTime).format('YYYY-MM-DD HH:mm:ss');
  500. return card;
  501. }
  502. function buildCardListFromRows(rows, folderList = [], isDeleteList = false) {
  503. const list = [];
  504. let card = null;
  505. let content = [];
  506. for (let i = 0; i <= rows.length; i++) {
  507. if (card != null && (i == rows.length || card.MiaoguoCardID != rows[i].MiaoguoCardID)) {
  508. card = formatMiaoguoCardTime(card, isDeleteList);
  509. card.Content = content;
  510. if (!isDeleteList)
  511. delete card.Flag;
  512. delete card.MiaoguoContentID;
  513. delete card.ContentType;
  514. delete card.MiaoguoDeckID;
  515. delete card.UserID;
  516. if (!isDeleteList) {
  517. delete card.HistoryUpdateTime;
  518. delete card.HistoryFirstTime;
  519. delete card.HistoryLastTime;
  520. delete card.HistoryLimitTime;
  521. delete card.HistoryIntervalDay;
  522. delete card.HistoryRepetitionCoefficient;
  523. delete card.HistoryLearnNumber;
  524. }
  525. if (folderList.length > 0) {
  526. const folder = folderList.find(item => item.ID == card.FolderID);
  527. if (folder)
  528. card.FolderName = folder.Name;
  529. }
  530. list.push(card);
  531. content = [];
  532. if (i == rows.length)
  533. break;
  534. }
  535. const item = rows[i];
  536. content.push({
  537. ContentType: item.ContentType,
  538. Content: stringUtils.ReplaceAllString(item.Content || '', "''", "'"),
  539. });
  540. card = { ...item };
  541. }
  542. return list;
  543. }
  544. async function getCachedMiaoguoCardList(param) {
  545. const cacheKey = "GetMiaoguoCard###UserID=" + param.UserID;
  546. const cacheValue = globalCache.get(cacheKey);
  547. if (cacheValue !== 0) {
  548. const list = typeof cacheValue === 'string' ? JSON.parse(cacheValue) : cacheValue;
  549. return list.map(item => formatMiaoguoCardTime({ ...item }));
  550. }
  551. const listID = await miaoguo.GetCardListID(param);
  552. const ids = listID.map(item => item.MiaoguoCardID);
  553. if (ids.length == 0)
  554. return [];
  555. const folderList = await miaoguo.GetUserFolderList(param);
  556. const rows = await miaoguo.GetCardList({
  557. UserID: param.UserID,
  558. IDArr: ids.join(','),
  559. OrderType: param.OrderType,
  560. });
  561. const list = buildCardListFromRows(rows, folderList);
  562. globalCache.set(cacheKey, JSON.stringify(list), config.BufferMemoryTimeLowNormal);
  563. return list;
  564. }
  565. async function getMiaoguoCardListResult(param) {
  566. if (param.CardType == 2)
  567. param.CardType = "all";
  568. let list = await getCachedMiaoguoCardList(param);
  569. let urgentCount = 0;
  570. let emptyCount = 0;
  571. for (const item of list) {
  572. if (item.CardType == 1)
  573. urgentCount++;
  574. else if (item.CardType == -1)
  575. emptyCount++;
  576. }
  577. const result = {
  578. Count: list.length,
  579. UrgentCount: urgentCount,
  580. EmptyCount: emptyCount,
  581. };
  582. if (param.DataType == -1) {
  583. result.List = list;
  584. return result;
  585. }
  586. if (param.IsToday == 2) {
  587. list = list.filter(item => item.LastTime >= moment().format('YYYY-MM-DD 00:00:00') && item.LimitTime >= moment().add(1, "days").format('YYYY-MM-DD 00:00:00'));
  588. result.Count = list.length;
  589. param.PageCount = list.length;
  590. } else if (param.IsToday == 3) {
  591. list = _.orderBy(list, ['LastTime'], ['desc']);
  592. let lastDate = "";
  593. const listTemp = [];
  594. for (const item of list) {
  595. if (item.LastTime < moment().format('YYYY-MM-DD 00:00:00')) {
  596. if (lastDate == "")
  597. lastDate = moment(item.LastTime).format('YYYY-MM-DD 00:00:00');
  598. if (item.LastTime >= lastDate)
  599. listTemp.push(item);
  600. }
  601. }
  602. list = listTemp;
  603. result.Count = list.length;
  604. param.PageCount = list.length;
  605. } else if (param.IsToday == 4) {
  606. list = _.orderBy(list, ['LimitTime'], ['desc'])
  607. .filter(item => item.LimitTime < moment().format('YYYY-MM-DD 23:59:59') && param.CardType == item.CardType);
  608. result.Count = list.length;
  609. } else if (param.IsToday == 5 || param.IsToday == 6 || param.IsToday == 7) {
  610. list = _.orderBy(list, ['LastTime'], ['desc']);
  611. const param3 = {
  612. UserID: param.UserID,
  613. CreateTime: moment().add(-param.DayNumber, "days").format('YYYY-MM-DD 00:00:00'),
  614. DataType: param.IsToday == 5 ? "TimeShort" : param.IsToday == 6 ? "ClickBlue" : "ClickShelve",
  615. };
  616. const listID = await miaoguo.GetCardListID(param3);
  617. const idSet = new Set(listID.map(item => Number(item.MiaoguoCardID)));
  618. list = list.filter(item => idSet.has(Number(item.MiaoguoCardID)));
  619. result.Count = list.length;
  620. } else {
  621. if (param.CardType != "all") {
  622. list = list.filter(item => item.CardType === Number(param.CardType));
  623. result.Count = list.length;
  624. }
  625. if (param.CardType == "all" && param.OrderType.indexOf("LimitTime") >= 0) {
  626. list = list.filter(item => item.CardType != -1);
  627. result.Count = list.length;
  628. }
  629. }
  630. if (param.IsCollect == 1) {
  631. list = list.filter(item => item.IsCollect == 1);
  632. result.Count = list.length;
  633. }
  634. if (param.FolderID) {
  635. list = list.filter(item => {
  636. if (param.FolderID > 0)
  637. return item.FolderID == param.FolderID;
  638. if (param.FolderID == -1)
  639. return item.FolderID === 0;
  640. return true;
  641. });
  642. result.Count = list.length;
  643. }
  644. if (param.Key) {
  645. const key = String(param.Key).toLowerCase();
  646. const listTemp = [];
  647. for (const item of list) {
  648. for (const content of item.Content || []) {
  649. if ((content.Content || '').toLowerCase().indexOf(key) >= 0) {
  650. listTemp.unshift(item);
  651. break;
  652. }
  653. }
  654. }
  655. for (let i = 0; i < listTemp.length; i++) {
  656. const primary = listTemp[i].Content && listTemp[i].Content[1] ? listTemp[i].Content[1].Content : "";
  657. if (primary && primary.toLowerCase().indexOf(key) >= 0) {
  658. const item = listTemp.splice(i, 1);
  659. listTemp.unshift(item[0]);
  660. }
  661. }
  662. list = listTemp;
  663. result.Count = list.length;
  664. }
  665. if (param.OrderType == "ac.UpdateTime desc,ac.CreateTime desc")
  666. list = _.orderBy(list, ['UpdateTime', 'CreateTime'], ['desc', 'desc']);
  667. else if (param.OrderType == "ac.MiaoguoCardID desc")
  668. list = _.orderBy(list, ['MiaoguoCardID'], ['desc']);
  669. else if (param.OrderType == "ac.MiaoguoCardID asc")
  670. list = _.orderBy(list, ['MiaoguoCardID'], ['asc']);
  671. else if (param.OrderType == "ac.LearnNumber desc,ac.MiaoguoCardID desc")
  672. list = _.orderBy(list, ['LearnNumber', 'MiaoguoCardID'], ['desc', 'desc']);
  673. else if (param.OrderType == "ac.LearnNumber,ac.MiaoguoCardID")
  674. list = _.orderBy(list, ['LearnNumber', 'MiaoguoCardID'], ['asc', 'asc']);
  675. else if (param.OrderType == "ac.LastTime desc")
  676. list = _.orderBy(list, ['LastTime'], ['desc']);
  677. else if (param.OrderType == "ac.LimitTime desc")
  678. list = _.orderBy(list, ['LimitTime'], ['desc']);
  679. else if (param.OrderType == "ac.LimitTime asc")
  680. list = _.orderBy(list, ['LimitTime'], ['asc']);
  681. else if (param.OrderType == "ac.PigeonholeTime asc")
  682. list = _.orderBy(list, ['PigeonholeTime'], ['asc']);
  683. else if (param.OrderType == "ac.PigeonholeTime desc")
  684. list = _.orderBy(list, ['PigeonholeTime'], ['desc']);
  685. const arr = [];
  686. if (param.IDSelect || param.PrintID) {
  687. if (param.PrintID && param.PrintID > 0) {
  688. const miaoguoPrint = await miaoguo.GetMiaoguoPrintByID(param);
  689. if (miaoguoPrint && miaoguoPrint.length > 0)
  690. param.IDSelect = miaoguoPrint[0].CardID;
  691. }
  692. const selectedIDs = String(param.IDSelect || '').split(',');
  693. for (const id of selectedIDs) {
  694. const item = list.find(card => String(card.MiaoguoCardID) == String(id));
  695. if (item)
  696. arr.push(item);
  697. }
  698. } else {
  699. let index = 0;
  700. let canPush = false;
  701. const pageIDForCompare = param.Type == "web" && param.PageID > 0 ? encryptWebID(param.PageID) : param.PageID;
  702. for (const item of list) {
  703. const cardIDForCompare = param.Type == "web" ? encryptWebID(item.MiaoguoCardID) : item.MiaoguoCardID;
  704. if (param.PageID == 0 || cardIDForCompare == pageIDForCompare)
  705. canPush = true;
  706. if (canPush) {
  707. if (param.PageID == 0) {
  708. arr.push(item);
  709. index++;
  710. if (index >= param.PageCount)
  711. break;
  712. } else {
  713. if (index > 0)
  714. arr.push(item);
  715. index++;
  716. if (index > param.PageCount)
  717. break;
  718. }
  719. }
  720. }
  721. }
  722. result.List = arr;
  723. if (param.Type == "web") {
  724. result.List = result.List.map(item => ({
  725. ...item,
  726. MiaoguoCardID: encryptWebID(item.MiaoguoCardID),
  727. }));
  728. if (param.IDSelect) {
  729. const miaoguoPrint = await miaoguo.AddMiaoguoPrint({
  730. UserID: param.UserID,
  731. CardID: param.IDSelect,
  732. });
  733. result.PrintID = miaoguoPrint.insertId;
  734. }
  735. }
  736. return result;
  737. }
  738. export async function GetMiaoguoCardListWeb(ctx) {
  739. const body = ctx.request.body || {};
  740. const param = {
  741. ...body,
  742. UserID: ctx.query.UserID || "0",
  743. PageID: body.PageID ? decryptWebID(body.PageID) : 0,
  744. PageCount: ctx.query.PageCount || "10",
  745. DataType: body.DataType || "",
  746. IsCollect: body.IsCollect == 1 ? 1 : 0,
  747. OrderType: body.OrderType || ctx.query.OrderType || "ac.UpdateTime desc",
  748. Type: "web",
  749. CardType: body.CardType || "2",
  750. FolderID: body.FolderID || 0,
  751. DayNumber: body.DayNumber || 7,
  752. };
  753. if (param.Key)
  754. param.PageCount = 500;
  755. else if (param.DataType)
  756. param.PageCount = 1000;
  757. if (body.IDSelect) {
  758. const ids = String(body.IDSelect).split(',').map(item => decryptWebID(item));
  759. param.IDSelect = ids.join(',');
  760. }
  761. const result = await getMiaoguoCardListResult(param);
  762. ctx.body = { errcode: 10000, result };
  763. }
  764. export async function GetMiaoguoCardList2(ctx) {
  765. const param = {
  766. UserID: ctx.query.UserID || "0",
  767. Key: ctx.query.Key || "",
  768. IsToday: ctx.query.IsToday || "0",
  769. OrderType: ctx.query.OrderType || "ac.UpdateTime desc,ac.CreateTime desc",
  770. PageID: ctx.query.PageID || 0,
  771. PageCount: ctx.query.PageCount || 20,
  772. IsCollect: ctx.query.IsCollect || 0,
  773. Type: ctx.query.Type || "app",
  774. DataType: ctx.query.DataType || "",
  775. CardType: ctx.query.CardType || "2",
  776. IDSelect: ctx.query.IDSelect || "",
  777. PrintID: ctx.query.PrintID || 0,
  778. FolderID: ctx.query.FolderID || 0,
  779. DayNumber: ctx.query.DayNumber || 7,
  780. };
  781. const result = await getMiaoguoCardListResult(param);
  782. ctx.body = { errcode: 10000, result };
  783. }
  784. function clearMiaoguoCardCache(userID) {
  785. globalCache.delete("GetMiaoguoCard###UserID=" + userID);
  786. }
  787. function addMiaoguoLog(log) {
  788. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  789. }
  790. function formatTodayCardTime(item) {
  791. item.CreateTime = moment(item.CreateTime).format('YYYY-MM-DD HH:mm:ss');
  792. item.CreateTimeStr = stringUtils.GetDateTimeFormat(item.CreateTime);
  793. item.UpdateTime = moment(item.UpdateTime).format('YYYY-MM-DD HH:mm:ss');
  794. item.UpdateTimeStr = stringUtils.GetDateTimeFormat(item.UpdateTime);
  795. item.FirstTime = moment(item.FirstTime).format('YYYY-MM-DD HH:mm:ss');
  796. item.LastTime = moment(item.LastTime).format('YYYY-MM-DD HH:mm:ss');
  797. item.LimitTime = moment(item.LimitTime).format('YYYY-MM-DD HH:mm:ss');
  798. item.PigeonholeTime = moment(item.PigeonholeTime).format('YYYY-MM-DD HH:mm:ss');
  799. return item;
  800. }
  801. function sm2(data, EF) {
  802. const result = Math.round(data * EF);
  803. return result < 1 ? 1 : result;
  804. }
  805. function computerMilestone(dayNumber0, dayNumber1, dayNumber2) {
  806. return Math.round(100 * (dayNumber0 - dayNumber1) / (dayNumber2 - dayNumber1)) / 100;
  807. }
  808. export async function GetMiaoguoCardInfo(ctx) {
  809. const param = {
  810. UserID: ctx.query.UserID || "0",
  811. MiaoguoCardID: ctx.query.MiaoguoCardID || "",
  812. };
  813. const info = await miaoguo.GetCardInfo(param);
  814. if (info && info.length > 0) {
  815. const listContent = await miaoguo.GetCardContentList(param.MiaoguoCardID);
  816. info[0].Content = listContent;
  817. info[0] = formatTodayCardTime(info[0]);
  818. }
  819. ctx.body = { errcode: 10000, result: info[0] };
  820. }
  821. export async function CollectMiaoguoCard(ctx) {
  822. const param = {
  823. UserID: ctx.query.UserIDSource || "0",
  824. UserIDTarget: ctx.query.UserIDTarget || "0",
  825. MiaoguoCardID: ctx.query.MiaoguoCardID || "",
  826. };
  827. let result = 0;
  828. const info = await miaoguo.GetCardInfo(param);
  829. if (info && info.length > 0) {
  830. const listContent = await miaoguo.GetCardContentList(param.MiaoguoCardID);
  831. if (listContent && listContent.length > 1) {
  832. param.Content = stringUtils.ReplaceSingleQuotes(listContent[1].Content || "");
  833. let count = await miaoguo.CheckCardInfoExist(param);
  834. if (count.length > 0) {
  835. if (param.MiaoguoCardID == count[0].MiaoguoCardID) {
  836. result = -1;
  837. } else {
  838. param.Content = stringUtils.ReplaceSingleQuotes(listContent[2]?.Content || "");
  839. param.CardID = count[0].MiaoguoCardID;
  840. count = await miaoguo.CheckCardInfoExist(param);
  841. if (count.length > 0) {
  842. param.Content = stringUtils.ReplaceSingleQuotes(listContent[3]?.Content || "");
  843. param.CardID = count[0].MiaoguoCardID;
  844. count = await miaoguo.CheckCardInfoExist(param);
  845. result = count.length > 0 ? 0 : 1;
  846. } else {
  847. result = 1;
  848. }
  849. }
  850. } else {
  851. result = 1;
  852. }
  853. }
  854. if (result == 1) {
  855. const now = moment().format('YYYY-MM-DD HH:mm:ss');
  856. const insertInfo = await miaoguo.AddCard({
  857. UserID: param.UserIDTarget,
  858. CreateTime: now,
  859. LimitTime: now,
  860. UpdateTime: now,
  861. CardType: -1,
  862. IsTodayStudy: 0,
  863. SourceCardID: param.MiaoguoCardID,
  864. });
  865. for (const content of listContent) {
  866. await miaoguo.AddCardContent({
  867. MiaoguoCardID: insertInfo.insertId,
  868. ContentType: content.ContentType,
  869. Content: stringUtils.ReplaceAllString(content.Content || "", "'", "''"),
  870. });
  871. }
  872. addMiaoguoLog({
  873. UserID: param.UserIDTarget,
  874. LogID: 316,
  875. Content: JSON.stringify({ ...param, NewMiaoguoCardID: insertInfo.insertId }),
  876. });
  877. }
  878. }
  879. clearMiaoguoCardCache(param.UserIDTarget);
  880. ctx.body = { errcode: 10000, result };
  881. }
  882. export async function SetGrade(ctx) {
  883. const param = {
  884. UserID: ctx.query.UserID || "0",
  885. Grade: ctx.query.Grade || 0,
  886. UpperAndLowerSemesters: ctx.query.UpperAndLowerSemesters || 0,
  887. };
  888. await miaoguo.UpdateUsersByUserID(param);
  889. const list = await miaoguo.GetCardListID(param);
  890. if (list.length == 0) {
  891. const folder = {
  892. UserID: param.UserID || 0,
  893. Name: "样例题卡",
  894. FolderType: 1,
  895. ColorName: "#FEAF3F",
  896. IconName: "cardlist_joy_white_80x80",
  897. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  898. Flag: 0,
  899. };
  900. const insertID = await miaoguo.AddFolderInfo(folder);
  901. const arrID = [];
  902. for (let i = 0; i < 10; i++) {
  903. const now = moment().format('YYYY-MM-DD HH:mm:ss');
  904. const insertInfo = await miaoguo.AddCard({
  905. UserID: param.UserID,
  906. CreateTime: now,
  907. LimitTime: now,
  908. UpdateTime: now,
  909. IsTodayStudy: 1,
  910. FolderID: insertID.insertId,
  911. });
  912. arrID.push(insertInfo.insertId);
  913. }
  914. let grade = param.Grade;
  915. if (grade == 0)
  916. grade = 1;
  917. if (grade == 100)
  918. grade = 7;
  919. const start = grade * 10 + 1;
  920. const end = grade * 10 + 10;
  921. let index = 0;
  922. for (let i = start; i <= end; i++) {
  923. const listContent = await miaoguo.GetCardContentList(i);
  924. for (const content of listContent) {
  925. await miaoguo.AddCardContent({
  926. MiaoguoCardID: arrID[index],
  927. ContentType: content.ContentType,
  928. Content: content.Content,
  929. });
  930. }
  931. index++;
  932. }
  933. }
  934. clearMiaoguoCardCache(param.UserID);
  935. setTimeout(() => {
  936. const sql = "select c.MiaoguoCardID from MiaoguoCard c left join MiaoguoContent mc on c.MiaoguoCardID=mc.MiaoguoCardID where c.UserID=? and mc.Content is null;";
  937. miaoguo.RunSql([param.UserID], sql).then(list => {
  938. for (const item of list)
  939. miaoguo.RunSql([item.MiaoguoCardID], "delete from MiaoguoCard where MiaoguoCardID=?;").catch(err => console.error('cleanup empty card failed:', err));
  940. }).catch(err => console.error('cleanup empty cards query failed:', err));
  941. }, 1000 * 60);
  942. ctx.body = { errcode: 10000 };
  943. }
  944. export async function GetMiaoguoCardToday2(ctx) {
  945. const param = {
  946. UserID: ctx.query.UserID || 0,
  947. };
  948. if (param.UserID == "undefined")
  949. param.UserID = 0;
  950. let result;
  951. if (param.UserID > 0) {
  952. const userConfig = await miaoguo.GetUserConfigData(param);
  953. if (userConfig && userConfig.length > 0) {
  954. param.BasicCardLimit = userConfig[0].BasicCardLimit;
  955. param.CardMaxNumberNew = userConfig[0].CardMaxNumberNew;
  956. param.CardMaxNumberHistory = userConfig[0].CardMaxNumberHistory;
  957. param.SortTypeIndex = userConfig[0].SortTypeIndex;
  958. param.CardMaxNumberNewUrgent = userConfig[0].CardMaxNumberNewUrgent;
  959. param.CardMaxNumberHistoryUrgent = userConfig[0].CardMaxNumberHistoryUrgent;
  960. param.CardTypeConfig = userConfig[0].CardType;
  961. } else {
  962. Object.assign(param, {
  963. TaskSetting: 0,
  964. BasicCardLimit: 50,
  965. CardMaxNumberNew: 10,
  966. CardMaxNumberHistory: 40,
  967. CardMaxNumberNewUrgent: 10,
  968. CardMaxNumberHistoryUrgent: 40,
  969. CardType: 0,
  970. SortTypeIndex: 0,
  971. MemoryLevel: 0,
  972. ClickType: 0,
  973. SecondConfigArray: "0,0,0",
  974. ColorIndexArr: "1,2,3,4",
  975. });
  976. await miaoguo.AddUserConfig(param);
  977. param.CardTypeConfig = 0;
  978. }
  979. const url = ctx.request.url + "&CardType=" + param.CardTypeConfig;
  980. result = globalCache.get(url);
  981. if (result === 0) {
  982. result = {};
  983. const param2 = { UserID: param.UserID, CardType: 0 };
  984. param2.Type = "NewLimitTimeToday";
  985. result.NewLimitTimeToday = (await miaoguo.GetMiaoguoCardTotal(param2))[0].Count;
  986. param2.Type = "HistoryLimitTimeToday";
  987. result.HistoryLimitTimeToday = (await miaoguo.GetMiaoguoCardTotal(param2))[0].Count;
  988. const todayStart = moment().format('YYYY-MM-DD 00:00:00');
  989. const tomorrowStart = moment().add(1, "days").format('YYYY-MM-DD 00:00:00');
  990. const practiceParam = {
  991. UserID: param.UserID,
  992. LimitTime: tomorrowStart,
  993. LastTime: todayStart,
  994. GetCount: true,
  995. };
  996. const alreadyPracticeNumber = await miaoguo.GetCardListID(practiceParam);
  997. result.AlreadyPracticeNumber = alreadyPracticeNumber[0].Count;
  998. if (param.BasicCardLimit > 0) {
  999. const historyPracticeParam = {
  1000. UserID: param.UserID,
  1001. LimitTime: tomorrowStart,
  1002. LastTime: todayStart,
  1003. GetCount: true,
  1004. CardType: 0,
  1005. IsNew: 0,
  1006. };
  1007. const alreadyPracticeNumberHistory = await miaoguo.GetCardListID(historyPracticeParam);
  1008. const alreadyPracticeNumberCountHistory = alreadyPracticeNumberHistory[0].Count;
  1009. let shouldUpdate = 0;
  1010. if (result.HistoryLimitTimeToday >= param.BasicCardLimit) {
  1011. param.CardMaxNumberNew = 0;
  1012. param.CardMaxNumberHistory = param.BasicCardLimit;
  1013. shouldUpdate = 1;
  1014. } else if (result.HistoryLimitTimeToday < param.BasicCardLimit) {
  1015. param.CardMaxNumberNew = param.BasicCardLimit - result.HistoryLimitTimeToday - alreadyPracticeNumberCountHistory;
  1016. if (param.CardMaxNumberNew < 0)
  1017. param.CardMaxNumberNew = 0;
  1018. param.CardMaxNumberHistory = result.HistoryLimitTimeToday + alreadyPracticeNumberCountHistory;
  1019. if (param.CardMaxNumberHistory > (param.BasicCardLimit + param.CardMaxNumberNew))
  1020. param.CardMaxNumberHistory = param.BasicCardLimit + param.CardMaxNumberNew;
  1021. shouldUpdate = 1;
  1022. }
  1023. if (shouldUpdate) {
  1024. await miaoguo.UpdateUserConfig({
  1025. UserID: param.UserID,
  1026. CardMaxNumberNew: param.CardMaxNumberNew,
  1027. CardMaxNumberHistory: param.CardMaxNumberHistory,
  1028. });
  1029. }
  1030. result.BasicCardLimit = param.BasicCardLimit;
  1031. } else {
  1032. result.BasicCardLimit = 0;
  1033. }
  1034. const arrID = [];
  1035. const listNew = [];
  1036. const listReview = [];
  1037. const listHistory = [];
  1038. result.UrgentNewLength = 0;
  1039. result.UrgentOldLength = 0;
  1040. result.CommonNewLength = 0;
  1041. result.CommonOldLength = 0;
  1042. async function appendTaskList(isUrgent) {
  1043. let list1 = [];
  1044. const list2 = [];
  1045. const list3 = [];
  1046. param.CardType = isUrgent ? 1 : 0;
  1047. let history = isUrgent ? param.CardMaxNumberHistoryUrgent : param.CardMaxNumberHistory;
  1048. if (isUrgent)
  1049. delete param.CardMaxNumberHistoryUrgent;
  1050. else
  1051. delete param.CardMaxNumberHistory;
  1052. param.LimitTime = tomorrowStart;
  1053. param.LastTime = todayStart;
  1054. const countNew = isUrgent ? await miaoguo.GetCardListTodayAlreadyUrgent(param) : await miaoguo.GetCardListTodayAlready(param);
  1055. if (isUrgent) {
  1056. param.CardMaxNumberNewUrgent = (param.CardMaxNumberNewUrgent - countNew[0].count) > 0 ? (param.CardMaxNumberNewUrgent - countNew[0].count) : 0;
  1057. param.SortType = "LimitTime asc,MiaoguoCardID asc";
  1058. if (param.CardMaxNumberNewUrgent > 0)
  1059. list1 = await miaoguo.GetCardListTodayUrgent(param);
  1060. } else {
  1061. param.CardMaxNumberNew = (param.CardMaxNumberNew - countNew[0].count) > 0 ? (param.CardMaxNumberNew - countNew[0].count) : 0;
  1062. if (param.SortTypeIndex == 0)
  1063. param.SortType = "RAND()";
  1064. else if (param.SortTypeIndex == 1)
  1065. param.SortType = "LimitTime asc,MiaoguoCardID asc";
  1066. else if (param.SortTypeIndex == 2)
  1067. param.SortType = "LimitTime desc,MiaoguoCardID desc";
  1068. if (param.CardMaxNumberNew > 0)
  1069. list1 = await miaoguo.GetCardListToday(param);
  1070. }
  1071. const newCount = isUrgent
  1072. ? Math.min(param.CardMaxNumberNewUrgent || 0, list1.length)
  1073. : Math.min(param.CardMaxNumberNew || 0, list1.length);
  1074. list1 = list1.slice(0, newCount).map(item => {
  1075. item.Number0 = "1m";
  1076. item.Number2 = "10m";
  1077. item.Number3 = "4d";
  1078. arrID.unshift(item.MiaoguoCardID);
  1079. return formatTodayCardTime(item);
  1080. });
  1081. param.IsReview = true;
  1082. const reviewList = isUrgent ? await miaoguo.GetCardListTodayUrgent(param) : await miaoguo.GetCardListToday(param);
  1083. for (const item of reviewList) {
  1084. item.Number0 = "10m";
  1085. item.Number2 = "1d";
  1086. item.Number3 = "4d";
  1087. list2.push(formatTodayCardTime(item));
  1088. arrID.unshift(item.MiaoguoCardID);
  1089. }
  1090. delete param.IsReview;
  1091. delete param.IsTodayStudy;
  1092. for (const item of reviewList) {
  1093. if (moment(item.FirstTime).format('YYYY-MM-DD HH:mm:ss') < param.LastTime)
  1094. history--;
  1095. }
  1096. if (history > 0) {
  1097. let oldCount;
  1098. if (isUrgent) {
  1099. param.CardMaxNumberHistoryUrgent = history;
  1100. oldCount = await miaoguo.GetCardListTodayAlreadyUrgent(param);
  1101. param.CardMaxNumberHistoryUrgent = (param.CardMaxNumberHistoryUrgent - oldCount[0].count) > 0 ? (param.CardMaxNumberHistoryUrgent - oldCount[0].count) : 0;
  1102. } else {
  1103. param.CardMaxNumberHistory = history;
  1104. oldCount = await miaoguo.GetCardListTodayAlready(param);
  1105. param.CardMaxNumberHistory = (param.CardMaxNumberHistory - oldCount[0].count) > 0 ? (param.CardMaxNumberHistory - oldCount[0].count) : 0;
  1106. }
  1107. const oldLimit = isUrgent ? param.CardMaxNumberHistoryUrgent : param.CardMaxNumberHistory;
  1108. if (oldLimit > 0) {
  1109. const oldList = isUrgent ? await miaoguo.GetCardListTodayUrgent(param) : await miaoguo.GetCardListToday(param);
  1110. const count = Math.min(oldLimit, oldList.length);
  1111. for (let i = 0; i < count; i++) {
  1112. const item = oldList[i];
  1113. item.Number0 = "10m";
  1114. const normal = sm2(item.IntervalDay, getEF(item.RepetitionCoefficient, 4));
  1115. item.Number1 = sm2(normal, Number(stringUtils.Random(2, 5)) / 10) + "d";
  1116. item.Number2 = normal + "d";
  1117. if (item.Number1 == item.Number2)
  1118. delete item.Number1;
  1119. let number3 = sm2(normal, item.RepetitionCoefficient);
  1120. if (normal + 1 > number3)
  1121. number3 = normal + 1;
  1122. item.Number3 = number3 + "d";
  1123. list3.push(formatTodayCardTime(item));
  1124. arrID.unshift(item.MiaoguoCardID);
  1125. }
  1126. }
  1127. }
  1128. if (isUrgent) {
  1129. result.UrgentNewLength = list1.length;
  1130. result.UrgentOldLength = list2.length + list3.length;
  1131. } else {
  1132. result.CommonNewLength = list1.length;
  1133. result.CommonOldLength = list3.length;
  1134. }
  1135. for (const item of list1)
  1136. item.IsUrgentCard = isUrgent ? 1 : 0;
  1137. for (const item of list2)
  1138. item.IsUrgentCard = isUrgent ? 1 : 0;
  1139. for (const item of list3)
  1140. item.IsUrgentCard = isUrgent ? 1 : 0;
  1141. listNew.push(...list1);
  1142. listReview.push(...list2);
  1143. listHistory.push(...list3);
  1144. }
  1145. if (param.CardTypeConfig == 1 || param.CardTypeConfig == 2)
  1146. await appendTaskList(true);
  1147. if (param.CardTypeConfig == 0 || param.CardTypeConfig == 2)
  1148. await appendTaskList(false);
  1149. if (arrID.length > 0) {
  1150. const listContent = await miaoguo.GetCardContentList(arrID.join(","));
  1151. const contentMap = new Map();
  1152. for (const content of listContent) {
  1153. if (!contentMap.has(content.MiaoguoCardID))
  1154. contentMap.set(content.MiaoguoCardID, []);
  1155. contentMap.get(content.MiaoguoCardID).push({
  1156. ContentType: content.ContentType,
  1157. Content: stringUtils.ReplaceAllString(content.Content || "", "''", "'"),
  1158. });
  1159. }
  1160. for (const list of [listNew, listReview, listHistory]) {
  1161. for (const item of list)
  1162. item.Content = contentMap.get(item.MiaoguoCardID) || [];
  1163. }
  1164. // 保证返回列表中每一项的 Content 均为 4 项,否则从列表中剔除,避免前端渲染异常
  1165. for (const list of [listNew, listReview, listHistory]) {
  1166. for (let i = list.length - 1; i >= 0; i--) {
  1167. if (!Array.isArray(list[i].Content) || list[i].Content.length !== 4)
  1168. list.splice(i, 1);
  1169. }
  1170. }
  1171. // 同步修正剔除后各项数量统计
  1172. result.UrgentNewLength = listNew.filter(item => item.IsUrgentCard === 1).length;
  1173. result.UrgentOldLength = listReview.filter(item => item.IsUrgentCard === 1).length + listHistory.filter(item => item.IsUrgentCard === 1).length;
  1174. result.CommonNewLength = listNew.filter(item => item.IsUrgentCard === 0).length;
  1175. result.CommonOldLength = listHistory.filter(item => item.IsUrgentCard === 0).length;
  1176. for (const list of [listNew, listReview, listHistory]) {
  1177. for (const item of list)
  1178. delete item.IsUrgentCard;
  1179. }
  1180. }
  1181. const dayNumberArr = await miaoguo.GetTaskData(param);
  1182. let dayNumber = (dayNumberArr || []).length + 1;
  1183. let isStart = 1;
  1184. const practiceCount = await miaoguo.GetTaskData({ UserID: param.UserID, CreateTime: todayStart });
  1185. if (practiceCount.length > 0 && practiceCount[0].CardCount > 0) {
  1186. isStart = 0;
  1187. if (dayNumber > 1)
  1188. dayNumber--;
  1189. }
  1190. const cardNumberArr = await miaoguo.GetCardCount(param);
  1191. const cardNumber = cardNumberArr && cardNumberArr.length > 0 ? cardNumberArr[0].count : 0;
  1192. const cardEveryTimeArr = await miaoguo.GetCardEveryTimeAvg({ UserID: param.UserID });
  1193. const cardEveryTime = cardEveryTimeArr && cardEveryTimeArr.length > 0 && cardEveryTimeArr[0].avg ? cardEveryTimeArr[0].avg : 20;
  1194. result.ListNew = listNew;
  1195. result.ListReview = listReview;
  1196. result.ListHistory = listHistory;
  1197. result.PracticeNumber = listNew.length * 2 + listReview.length + listHistory.length;
  1198. result.PracticeTime = Math.round(result.PracticeNumber * cardEveryTime / 60);
  1199. if (result.PracticeTime == 0 && result.PracticeNumber > 0)
  1200. result.PracticeTime = 1;
  1201. result.DayNumber = dayNumber;
  1202. result.CardNumber = cardNumber;
  1203. result.IsStart = isStart;
  1204. result.Today = moment().format('YYYY-MM-DD');
  1205. const cardStudyPlanNumber = await miaoguo.GetStudyPlanCardNumber(param);
  1206. result.CardNumberCommon = 0;
  1207. result.CardNumberUrgent = 0;
  1208. for (const item of cardStudyPlanNumber) {
  1209. if (item.CardType === 0)
  1210. result.CardNumberCommon = item.Count;
  1211. else if (item.CardType === 1)
  1212. result.CardNumberUrgent = item.Count;
  1213. }
  1214. const today = moment().format('YYYYMMDD');
  1215. for (const list of [result.ListNew, result.ListReview, result.ListHistory]) {
  1216. for (const item of list) {
  1217. const historyUpdateTime = moment(item.HistoryUpdateTime).format('YYYYMMDD');
  1218. if (today > historyUpdateTime) {
  1219. miaoguo.UpdateCard({
  1220. MiaoguoCardID: item.MiaoguoCardID,
  1221. HistoryUpdateTime: moment().format('YYYY-MM-DD 00:00:00'),
  1222. HistoryFirstTime: item.FirstTime,
  1223. HistoryLastTime: item.LastTime,
  1224. HistoryLimitTime: item.LimitTime,
  1225. HistoryIntervalDay: item.IntervalDay,
  1226. HistoryRepetitionCoefficient: item.RepetitionCoefficient,
  1227. HistoryLearnNumber: item.LearnNumber,
  1228. }).catch(err => console.error('UpdateCard history failed:', err));
  1229. }
  1230. }
  1231. }
  1232. const listFolder = await miaoguo.GetUserFolderList(param);
  1233. result.FolderName = [{ FolderID: 0, FolderName: "" }];
  1234. for (const folder of listFolder)
  1235. result.FolderName.push({ FolderID: folder.ID, FolderName: folder.Name });
  1236. globalCache.set(url, result, config.BufferMemoryTimeLower || config.BufferMemoryTimeLowNormal);
  1237. }
  1238. } else {
  1239. result = { IsMaintain: 1 };
  1240. }
  1241. ctx.body = { errcode: 10000, result };
  1242. }
  1243. export async function DeleteMiaoguoCard(ctx) {
  1244. const param = {
  1245. MiaoguoCardID: ctx.query.ID,
  1246. UserID: ctx.query.UserID,
  1247. Flag: -1,
  1248. DeleteTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  1249. };
  1250. param.UpdateTime = param.DeleteTime;
  1251. await miaoguo.UpdateCard(param);
  1252. addMiaoguoLog({ UserID: ctx.query.UserID, LogID: 299, Content: JSON.stringify(param) });
  1253. clearMiaoguoCardCache(ctx.query.UserID);
  1254. ctx.body = { errcode: 10000 };
  1255. }
  1256. export async function StartMiaoguoTask(ctx) {
  1257. const param = {
  1258. UserID: ctx.query.UserID || 0,
  1259. CurrentUserID: ctx.query.CurrentUserID,
  1260. };
  1261. addMiaoguoLog({ UserID: ctx.query.UserID, LogID: 304, Content: JSON.stringify(param) });
  1262. ctx.body = { errcode: 10000 };
  1263. }
  1264. export async function ResetMiaoguoCardTask(ctx) {
  1265. const param = { UserID: ctx.query.UserID };
  1266. const list = await miaoguo.GetCardTodayFinished(param);
  1267. const arr = [];
  1268. for (const item of list) {
  1269. await miaoguo.UpdateCard({
  1270. MiaoguoCardID: item.MiaoguoCardID,
  1271. FirstTime: item.HistoryFirstTime,
  1272. LastTime: item.HistoryLastTime,
  1273. LimitTime: item.HistoryLimitTime,
  1274. IntervalDay: item.HistoryIntervalDay,
  1275. RepetitionCoefficient: item.HistoryRepetitionCoefficient,
  1276. LearnNumber: item.HistoryLearnNumber,
  1277. });
  1278. arr.push(item.MiaoguoCardID);
  1279. }
  1280. if (arr.length > 0)
  1281. await miaoguo.ResetTodayFinished({ CardID: arr.join(",") });
  1282. await miaoguo.DeleteMiaoguoTask(param);
  1283. addMiaoguoLog({ UserID: ctx.query.UserID, LogID: 317, Content: JSON.stringify(param) });
  1284. clearMiaoguoCardCache(ctx.query.UserID);
  1285. ctx.body = { errcode: 10000, result: true };
  1286. }
  1287. export async function RemoveFolder(ctx) {
  1288. const param = {
  1289. UserID: ctx.query.UserID || 0,
  1290. MiaoguoCardID: ctx.query.ID || 0,
  1291. FolderID: ctx.query.FolderID || 0,
  1292. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  1293. };
  1294. await miaoguo.UpdateFolderInfo({
  1295. UserID: param.UserID,
  1296. ID: param.FolderID,
  1297. UpdateTime: param.UpdateTime,
  1298. });
  1299. param.FolderID = 0;
  1300. await miaoguo.UpdateCard(param);
  1301. addMiaoguoLog({ UserID: ctx.query.UserID, LogID: 311, Content: JSON.stringify(param) });
  1302. clearMiaoguoCardCache(ctx.query.UserID);
  1303. ctx.body = { errcode: 10000 };
  1304. }
  1305. export async function UpdateMiaoguoCardType(ctx) {
  1306. const param = {
  1307. UserID: ctx.query.UserID || 0,
  1308. MiaoguoCardID: ctx.query.ID || 0,
  1309. CardType: ctx.query.CardType || 0,
  1310. FolderType: ctx.query.FolderType || 0,
  1311. LimitTime: ctx.query.LimitTime || "",
  1312. };
  1313. if (param.LimitTime === "" || param.LimitTime == "undefined")
  1314. delete param.LimitTime;
  1315. await miaoguo.UpdateCardType(param);
  1316. if (param.FolderType >= 1) {
  1317. const folderInfo = await miaoguo.GetMiaoguoFolderCount(param);
  1318. let folderID = 0;
  1319. if (folderInfo.length > 0) {
  1320. folderID = folderInfo[0].ID;
  1321. } else {
  1322. const insertID = await miaoguo.AddFolderInfo({
  1323. UserID: ctx.query.UserID || 0,
  1324. Name: "练习中不会的题卡",
  1325. FolderType: 1,
  1326. ColorName: "#FEAF3F",
  1327. IconName: "cardlist_joy_white_80x80",
  1328. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  1329. Flag: 0,
  1330. });
  1331. folderID = insertID.insertId;
  1332. }
  1333. await miaoguo.UpdateCard({ MiaoguoCardID: param.MiaoguoCardID, FolderID: folderID });
  1334. }
  1335. addMiaoguoLog({
  1336. UserID: ctx.query.UserID,
  1337. LogID: param.FolderType && param.FolderType >= 1 ? 313 : 301,
  1338. Content: JSON.stringify(param),
  1339. });
  1340. clearMiaoguoCardCache(ctx.query.UserID);
  1341. ctx.body = { errcode: 10000 };
  1342. }
  1343. export async function GetMiaoguoTaskTime(ctx) {
  1344. const param = { UserID: ctx.query.UserID };
  1345. param.CreateTime = moment().format('YYYY-MM-DD 00:00:00');
  1346. const time = await miaoguo.GetTaskData(param);
  1347. delete param.CreateTime;
  1348. const dayNumberArr = await miaoguo.GetTaskData(param);
  1349. let dayNumber = dayNumberArr.length;
  1350. if (dayNumber == 0)
  1351. dayNumber = 1;
  1352. if (time && time.length > 0) {
  1353. time[0].DayNumber = dayNumber;
  1354. if (time[0].CardCount > 0 && time[0].Duration > 0) {
  1355. param.CreateTime = moment().format('YYYY-MM-DD 00:00:00');
  1356. const obj = {
  1357. UserID: param.UserID,
  1358. CreateTime: param.CreateTime,
  1359. CardTask: time[0].CardCount,
  1360. Duration: time[0].Duration,
  1361. };
  1362. const tasks = await miaoguo.GetUserMiaoguoTask(param);
  1363. if (tasks && tasks.length > 0) {
  1364. // 当天任务已存在,dayNumberArr 已包含当天记录,直接用累计练习天数
  1365. obj.DayNumber = dayNumberArr.length;
  1366. await miaoguo.UpdateMiaoguoTask(obj);
  1367. } else {
  1368. // 新增当天任务,DayNumber 为已有累计天数 + 1(注意用原始长度,新用户首日应为 1)
  1369. obj.DayNumber = dayNumberArr.length + 1;
  1370. await miaoguo.AddMiaoguoTask(obj);
  1371. await updateMiaoguoMilestoneInfoByUserID(param.UserID);
  1372. }
  1373. }
  1374. if (!time[0].Duration || time[0].Duration < 60)
  1375. time[0].Duration = 60;
  1376. time[0].Count = time[0].CardCount;
  1377. }
  1378. ctx.body = { errcode: 10000, result: time[0] };
  1379. }
  1380. export async function GetMiaoguoCardTotal(ctx) {
  1381. const param = {
  1382. UserID: ctx.query.UserID || 0,
  1383. CardType: ctx.query.CardType || 0,
  1384. };
  1385. const result = {};
  1386. param.Type = "NewAll";
  1387. result.NewCardAll = (await miaoguo.GetMiaoguoCardTotal(param))[0].Count;
  1388. param.Type = "HistoryAll";
  1389. result.HistoryCardAll = (await miaoguo.GetMiaoguoCardTotal(param))[0].Count;
  1390. param.Type = "NewToday";
  1391. result.NewToday = (await miaoguo.GetMiaoguoCardTotal(param))[0].Count;
  1392. param.Type = "NewLimitTimeToday";
  1393. result.NewLimitTimeToday = (await miaoguo.GetMiaoguoCardTotal(param))[0].Count;
  1394. result.NewCardNumber = result.NewLimitTimeToday;
  1395. param.Type = "HistoryLimitTimeToday";
  1396. result.HistoryLimitTimeToday = (await miaoguo.GetMiaoguoCardTotal(param))[0].Count;
  1397. result.LimitCardNumber = result.HistoryLimitTimeToday;
  1398. param.Type = "NewAndLimitToday";
  1399. result.NewCardNumberAndLimitToday = (await miaoguo.GetMiaoguoCardTotal(param))[0].Count;
  1400. param.Type = "HistoryAndLimitToday";
  1401. result.HistoryCardNumberAndLimitToday = (await miaoguo.GetMiaoguoCardTotal(param))[0].Count;
  1402. result.LimitCardNumberToday = result.HistoryCardNumberAndLimitToday;
  1403. ctx.body = { errcode: 10000, result };
  1404. }
  1405. export async function GetMiaoguoPayInfo3(ctx) {
  1406. const param = {
  1407. UserID: ctx.query.UserID,
  1408. PayType: ctx.query.PayType || 1,
  1409. EndDate: ctx.query.EndDate,
  1410. Price: ctx.query.Price || 0,
  1411. DayNumber: ctx.query.DayNumber || 0,
  1412. };
  1413. const price2 = 199;
  1414. const price2Str = "元 (55折,原价365元)";
  1415. const price3 = 258;
  1416. const price3Str = "元 (7折,原价365元)";
  1417. if (param.EndDate && param.EndDate.indexOf("年") > 0)
  1418. param.EndDate = param.EndDate.replace("年", "-").replace("月", "-").replace("日", "");
  1419. let time1 = moment(new Date(param.EndDate)).format('YYYY-MM-DD');
  1420. const today = moment().format('YYYY-MM-DD');
  1421. if (time1 == "Invalid date" || time1 < today)
  1422. time1 = today;
  1423. const userList = await miaoguo.GetUsersInfoByUserID(param);
  1424. if (userList && userList.length > 0) {
  1425. const productServiceTime = moment(userList[0].ProductServiceTime).format('YYYY-MM-DD');
  1426. if (time1 < productServiceTime)
  1427. time1 = productServiceTime;
  1428. }
  1429. const time2 = moment(time1).add(12, 'months').add(30, 'days').format('YYYY年MM月DD日');
  1430. const time5 = moment(time1).format('YYYY年MM月DD日');
  1431. const result = {};
  1432. if (param.PayType == 2) {
  1433. result.Text1 = "购买";
  1434. result.List1 = [
  1435. { Name: "秒过年费", Content: price2 + price2Str },
  1436. { Name: "赠送时间", Content: "30天" },
  1437. { Name: "有效期", Content: time2 },
  1438. ];
  1439. result.Text2 = "支付前请注意:\n自支付之日起7天内接受无理由退款,此期限过后不予退款。\n支付即代表您接受本条款。";
  1440. result.Text8 = "支付";
  1441. result.Text3 = "支付成功";
  1442. result.Text4 = "";
  1443. result.Text5 = "请点开通";
  1444. result.Text6 = "";
  1445. result.Text7 = "";
  1446. result.PayMoney = price2;
  1447. } else if (param.PayType == 5) {
  1448. result.Text1 = "购买";
  1449. let price = price3;
  1450. let priceStr = price3Str;
  1451. if (userList[0] && userList[0].IsPay == 1) {
  1452. price = price2;
  1453. priceStr = price2Str;
  1454. }
  1455. result.List1 = [
  1456. { Name: "秒过年费", Content: price + priceStr },
  1457. { Name: "有效期", Content: time5 },
  1458. ];
  1459. result.Text2 = "支付前请注意:\n自支付之日起7天内接受无理由退款,此期限过后不予退款。\n支付即代表您接受本条款。";
  1460. result.Text8 = "支付";
  1461. result.Text3 = "支付成功";
  1462. result.Text4 = "";
  1463. result.Text5 = "请点开通";
  1464. result.Text6 = "";
  1465. result.Text7 = "";
  1466. result.PayMoney = price;
  1467. } else if (param.PayType == 6) {
  1468. const time6 = moment(time1).add(param.DayNumber, 'days').format('YYYY年MM月DD日');
  1469. result.Text1 = "购买";
  1470. if (param.Price == 0)
  1471. param.Price = price2;
  1472. let priceStr = "元";
  1473. if (param.Price == price2)
  1474. priceStr = price2Str;
  1475. else if (param.Price == price3)
  1476. priceStr = price3Str;
  1477. result.List1 = [
  1478. { Name: "秒过年费", Content: param.Price + priceStr },
  1479. { Name: "有效期", Content: time6 },
  1480. ];
  1481. result.Text2 = "支付前请注意:\n自支付之日起7天内接受无理由退款,此期限过后不予退款。\n支付即代表您接受本条款。";
  1482. result.Text8 = "支付";
  1483. result.Text3 = "支付成功";
  1484. result.Text4 = "";
  1485. result.Text5 = "请点开通";
  1486. result.Text6 = "";
  1487. result.Text7 = "";
  1488. result.PayMoney = param.Price;
  1489. } else if (param.PayType == 7) {
  1490. result.Text1 = "购买";
  1491. result.List1 = [
  1492. { Name: "秒过年费", Content: price2 + price2Str },
  1493. { Name: "有效期", Content: time5 },
  1494. ];
  1495. result.Text2 = "支付前请注意:\n自支付之日起7天内接受无理由退款,此期限过后不予退款。\n支付即代表您接受本条款。";
  1496. result.Text8 = "支付";
  1497. result.Text3 = "支付成功";
  1498. result.Text4 = "";
  1499. result.Text5 = "请点开通";
  1500. result.Text6 = "";
  1501. result.Text7 = "";
  1502. result.PayMoney = price2;
  1503. }
  1504. ctx.body = { errcode: 10000, result };
  1505. }
  1506. export async function InsertMiaoguoUserLesson(ctx) {
  1507. const param = {
  1508. UserID: ctx.query.UserID || 0,
  1509. LessonID: ctx.query.LessonID || 1,
  1510. CreateTime: moment().format('YYYY-MM-DD HH:mm:ss'),
  1511. Content: ctx.query.Content,
  1512. };
  1513. if (param.Content)
  1514. await miaoguo.AddMiaoguoUserLesson(param);
  1515. ctx.body = { errcode: 10000 };
  1516. }
  1517. export async function UpdateFolderInfo(ctx) {
  1518. const param = {
  1519. UserID: ctx.query.UserID || 0,
  1520. ID: ctx.query.FolderID || 0,
  1521. Name: ctx.query.Name || "",
  1522. Flag: ctx.query.Flag || 0,
  1523. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  1524. };
  1525. if (ctx.query.ColorName)
  1526. param.ColorName = "#" + ctx.query.ColorName;
  1527. if (ctx.query.IconName)
  1528. param.IconName = ctx.query.IconName;
  1529. if (param.ID > 0) {
  1530. if (param.Flag == -1) {
  1531. delete param.Name;
  1532. delete param.ColorName;
  1533. delete param.IconName;
  1534. }
  1535. await miaoguo.UpdateFolderInfo(param);
  1536. if (param.Flag == -1) {
  1537. param.FolderOldID = param.ID;
  1538. param.FolderID = 0;
  1539. await miaoguo.UpdateFolderList(param);
  1540. clearMiaoguoCardCache(ctx.query.UserID);
  1541. }
  1542. addMiaoguoLog({ UserID: ctx.query.UserID, LogID: 310, Content: JSON.stringify(param) });
  1543. } else {
  1544. delete param.ID;
  1545. await miaoguo.AddFolderInfo(param);
  1546. addMiaoguoLog({ UserID: ctx.query.UserID, LogID: 309, Content: JSON.stringify(param) });
  1547. }
  1548. ctx.body = { errcode: 10000 };
  1549. }
  1550. export async function ShareMiaoguoCard(ctx) {
  1551. const param = {
  1552. UserID: ctx.query.UserID || 0,
  1553. MiaoguoCardID: ctx.query.MiaoguoCardID || 0,
  1554. IsShare: 1,
  1555. };
  1556. await miaoguo.UpdateCard(param);
  1557. ctx.body = { errcode: 10000 };
  1558. }
  1559. export async function BuildMiaoguoQRCode(ctx) {
  1560. const param = {
  1561. UserID: ctx.query.UserID || 0,
  1562. PrintID: ctx.query.PrintID || 0,
  1563. IsWeb: ctx.query.IsWeb || 0,
  1564. };
  1565. try {
  1566. const tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?appid=' + config.wx.miaoguo_appid + '&secret=' + config.wx.miaoguo_appsecret + '&grant_type=client_credential';
  1567. const tokenRes = await axios.get(tokenUrl, { transformResponse: [(data) => data] });
  1568. const tokenJson = typeof tokenRes.data === 'string' ? JSON.parse(tokenRes.data) : tokenRes.data;
  1569. if (!tokenJson || !tokenJson.access_token) {
  1570. ctx.body = { errcode: 10000 };
  1571. return;
  1572. }
  1573. const postJSON = {};
  1574. let imageUrl = "";
  1575. if (param.PrintID && param.PrintID > 0) {
  1576. postJSON.width = 280;
  1577. postJSON.scene = "type=print&P=" + param.PrintID + "&U=" + param.UserID;
  1578. imageUrl = 'MiaoguoPrint' + param.PrintID + '.png';
  1579. } else if (param.IsWeb) {
  1580. postJSON.width = 280;
  1581. postJSON.scene = "UserID=" + param.UserID;
  1582. let userID = Encrypt(param.UserID, config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
  1583. userID = stringUtils.ReplaceAllString(userID, "/", "___");
  1584. userID = stringUtils.ReplaceAllString(userID, "+", "___");
  1585. imageUrl = 'MiaoguoUser' + userID + '.png';
  1586. miaoguo.UpdateUsersByUserID({ HasACode: 1, UserID: param.UserID }).catch(err => console.error('UpdateUsersByUserID HasACode failed:', err));
  1587. }
  1588. if (imageUrl) {
  1589. const qrUrl = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=' + tokenJson.access_token;
  1590. const dir = './public/images/acode';
  1591. await fsPromises.mkdir(dir, { recursive: true });
  1592. const filePath = path.join(dir, imageUrl);
  1593. const qrRes = await axios.post(qrUrl, postJSON, {
  1594. responseType: 'arraybuffer',
  1595. transformResponse: [(data) => data],
  1596. });
  1597. await fsPromises.writeFile(filePath, Buffer.from(qrRes.data));
  1598. await uploadMiaoguoFile(imageUrl, filePath);
  1599. }
  1600. } catch (err) {
  1601. console.error('BuildMiaoguoQRCode failed:', err);
  1602. }
  1603. ctx.body = { errcode: 10000 };
  1604. }
  1605. async function updateMiaoguoMilestoneInfoByUserID(userID) {
  1606. const param = {
  1607. UserID: userID || 0,
  1608. FieldStr: "*",
  1609. OrderType: "desc",
  1610. };
  1611. const list = await miaoguo.GetUserMiaoguoTaskByUserID(param);
  1612. if (!list || list.length == 0)
  1613. return;
  1614. const count = list.length;
  1615. const arrHasMilestoneID = list.filter(item => item.MilestoneID).map(item => item.MilestoneID);
  1616. const mileStoneArr = constantClass.GetMilestone();
  1617. const maxMileStoneID = mileStoneArr.length;
  1618. const mArr = [];
  1619. for (let i = 1; i < maxMileStoneID; i++) {
  1620. if (!arrHasMilestoneID.includes(i))
  1621. mArr.push(i);
  1622. }
  1623. const mID = mArr.length > 0 ? mArr[stringUtils.Random(0, mArr.length - 1)] : stringUtils.Random(1, maxMileStoneID);
  1624. for (const milestone of mileStoneArr) {
  1625. if (milestone.DayNumber == count) {
  1626. await miaoguo.UpdateMiaoguoTask({
  1627. ID: list[0].ID,
  1628. MilestoneDayNumber: milestone.DayNumber,
  1629. MilestoneID: mID,
  1630. });
  1631. break;
  1632. }
  1633. }
  1634. }
  1635. export async function UpdateMiaoguoMilestoneInfo(ctx) {
  1636. await updateMiaoguoMilestoneInfoByUserID(ctx.query.UserID || 0);
  1637. ctx.body = { errcode: 10000 };
  1638. }
  1639. export async function GetHardworkingList(ctx) {
  1640. const param = {
  1641. UserID: ctx.query.UserID || 0,
  1642. OrderType: "desc",
  1643. };
  1644. const cacheKey = "GetHardworkingList?UserID=" + param.UserID;
  1645. let result = globalCache.get(cacheKey);
  1646. if (result === 0) {
  1647. result = {};
  1648. param.IsHardworking = true;
  1649. const dayNumberArr = await miaoguo.GetTaskData(param);
  1650. result.DayNumber = dayNumberArr[0].Count;
  1651. if (result.DayNumber > 0) {
  1652. const mileStoneArr = constantClass.GetMilestone();
  1653. for (let i = 0; i < mileStoneArr.length; i++) {
  1654. if (result.DayNumber < mileStoneArr[i].DayNumber) {
  1655. result.MilestonePrev = mileStoneArr[i - 1];
  1656. result.MilestoneNext = mileStoneArr[i];
  1657. result.RemainingDays = mileStoneArr[i].DayNumber - result.DayNumber;
  1658. result.PastDays = result.DayNumber - mileStoneArr[i - 1].DayNumber;
  1659. result.Rate = 100 * computerMilestone(result.DayNumber, mileStoneArr[i - 1].DayNumber, mileStoneArr[i].DayNumber) + "%";
  1660. break;
  1661. }
  1662. }
  1663. const createTime = moment().format("YYYY-MM") + "-01";
  1664. param.FieldStr = "ID,CreateTime,IsShare,HardworkingNumber,MilestoneDayNumber,MilestoneID,AphorismID,ImageID";
  1665. result.SevenDays = [];
  1666. result.CurrentMonth = [];
  1667. const list = await miaoguo.GetUserMiaoguoTaskByUserID(param);
  1668. if (list && list.length > 0) {
  1669. const arrAphorism = await miaoguo.GetAphorism();
  1670. for (const item of list) {
  1671. if (item.AphorismID > 0 || item.MilestoneDayNumber > 0) {
  1672. const obj = {
  1673. ID: item.ID,
  1674. HardworkingNumber: item.HardworkingNumber,
  1675. OrdinalPostfix: stringUtils.GetOrdinalPostfix(item.HardworkingNumber),
  1676. };
  1677. if (item.MilestoneDayNumber > 0) {
  1678. for (const milestone of mileStoneArr) {
  1679. if (milestone.DayNumber == item.MilestoneDayNumber) {
  1680. obj.Name = milestone.Name;
  1681. obj.Color2 = milestone.Color2;
  1682. obj.IsMilestone = 1;
  1683. break;
  1684. }
  1685. }
  1686. } else {
  1687. obj.Image = "https://miaguo-1253256735.file.myqcloud.com/web/_share/qmjl_pic_d_" + item.ImageID + ".jpg";
  1688. if (item.AphorismID) {
  1689. for (const aphorism of arrAphorism) {
  1690. if (aphorism.ID == item.AphorismID) {
  1691. obj.Content = aphorism.Content;
  1692. obj.AphorismID = item.AphorismID;
  1693. break;
  1694. }
  1695. }
  1696. }
  1697. }
  1698. obj.CreateTime = moment(item.CreateTime).format("YYYY.MM.DD");
  1699. result.SevenDays.push(obj);
  1700. if (result.SevenDays.length >= 7)
  1701. break;
  1702. }
  1703. }
  1704. let shareNumber = 0;
  1705. let shareNumberYear = 0;
  1706. let shareNumberYearPrev = 0;
  1707. let shareNumberMonth = 0;
  1708. for (let i = list.length - 1; i >= 0; i--) {
  1709. const item = list[i];
  1710. item.CreateTime = moment(item.CreateTime).format("YYYY-MM-DD");
  1711. if (item.CreateTime >= createTime) {
  1712. const obj = {
  1713. ID: item.ID,
  1714. IsShare: item.IsShare,
  1715. Day: moment(item.CreateTime).format("D"),
  1716. HardworkingNumber: item.HardworkingNumber,
  1717. };
  1718. if (item.MilestoneDayNumber > 0) {
  1719. for (const milestone of mileStoneArr) {
  1720. if (milestone.DayNumber == item.MilestoneDayNumber) {
  1721. obj.Name = milestone.Name;
  1722. obj.Color1 = milestone.Color1;
  1723. obj.IsMilestone = 1;
  1724. break;
  1725. }
  1726. }
  1727. }
  1728. if (item.AphorismID > 0 && item.ImageID) {
  1729. obj.Image = "https://miaguo-1253256735.file.myqcloud.com/web/_share/qmjl_pic_c_" + item.ImageID + ".jpg";
  1730. obj.AphorismID = item.AphorismID;
  1731. }
  1732. if (item.IsShare || item.AphorismID > 0 || item.MilestoneDayNumber > 0)
  1733. shareNumberMonth++;
  1734. result.CurrentMonth.push(obj);
  1735. }
  1736. if (item.IsShare || item.AphorismID > 0 || item.MilestoneDayNumber > 0) {
  1737. shareNumber++;
  1738. if (item.CreateTime >= moment().format("YYYY-01-01") && item.IsShare)
  1739. shareNumberYear++;
  1740. if (item.CreateTime >= "2022-01-01" && item.CreateTime < "2023-01-01" && item.IsShare)
  1741. shareNumberYearPrev++;
  1742. }
  1743. }
  1744. result.ShareNumberMonth = shareNumberMonth;
  1745. result.ShareNumberYear = shareNumberYear;
  1746. result.ShareNumberYearPrev = shareNumberYearPrev;
  1747. result.ShareNumberYearRate = Math.round(100 * shareNumberYear / 200);
  1748. if (result.ShareNumberYearRate > 100)
  1749. result.ShareNumberYearRate = 100;
  1750. result.Award = 0;
  1751. if (result.ShareNumberYearPrev >= 100)
  1752. result.Award = 30;
  1753. if (result.ShareNumberYearPrev >= 200)
  1754. result.Award = 60;
  1755. result.ShareNumber = shareNumber;
  1756. result.ShareFirstDay = moment(list[list.length - 1].CreateTime).format("YYYY年MM月DD日");
  1757. }
  1758. }
  1759. globalCache.set(cacheKey, result, config.BufferMemoryTime);
  1760. }
  1761. ctx.body = { errcode: 10000, result };
  1762. }
  1763. export async function GetMilestoneList(ctx) {
  1764. const param = {
  1765. UserID: ctx.query.UserID || 0,
  1766. OrderType: "asc",
  1767. FieldStr: "*",
  1768. };
  1769. const result = {};
  1770. const list = await miaoguo.GetUserMiaoguoTaskByUserID(param);
  1771. if (list && list.length > 0) {
  1772. const mileStoneArr = constantClass.GetMilestone();
  1773. result.DayNumber = list.length;
  1774. result.MilestoneFinished = [];
  1775. let mIndex = 0;
  1776. let mPosition = "";
  1777. for (const item of list) {
  1778. for (let j = 0; j < mileStoneArr.length; j++) {
  1779. if (mileStoneArr[j].DayNumber == item.MilestoneDayNumber) {
  1780. const obj = {
  1781. ID: item.ID,
  1782. Name: mileStoneArr[j].Name,
  1783. DayNumber: item.MilestoneDayNumber,
  1784. Color: mileStoneArr[j].Color1,
  1785. Date: moment(item.CreateTime).format("YYYY.MM.DD"),
  1786. };
  1787. if (mileStoneArr[j].DayNumber == 1) {
  1788. obj.Position = "center";
  1789. } else {
  1790. mPosition = j / 2 == Math.floor(j / 2) ? "left" : "right";
  1791. obj.Position = mPosition + ":400rpx;";
  1792. }
  1793. result.MilestoneFinished.push(obj);
  1794. mIndex = j;
  1795. break;
  1796. }
  1797. }
  1798. }
  1799. if (result.MilestoneFinished.length > 0 && result.MilestoneFinished[result.MilestoneFinished.length - 1].Position == "right:400rpx;") {
  1800. for (const item of result.MilestoneFinished)
  1801. item.Position = item.Position == "left:400rpx;" ? "right:400rpx;" : "left:400rpx;";
  1802. }
  1803. result.MilestoneRemain = [];
  1804. for (let j = mIndex + 1; j < mileStoneArr.length; j++) {
  1805. const item = { ...mileStoneArr[j] };
  1806. delete item.Color1;
  1807. delete item.Color2;
  1808. result.MilestoneRemain.push(item);
  1809. }
  1810. if (result.MilestoneFinished.length > 0 && result.MilestoneRemain.length > 0) {
  1811. result.RemainRate = computerMilestone(list.length, result.MilestoneFinished[result.MilestoneFinished.length - 1].DayNumber, result.MilestoneRemain[0].DayNumber);
  1812. result.RemainRateStr = Math.round(100 * result.RemainRate) + "%";
  1813. result.MilestoneFinished.push({
  1814. ID: list[0].ID,
  1815. Name: result.MilestoneRemain[0].Name,
  1816. HardworkingNumber: result.MilestoneRemain[0].DayNumber,
  1817. Position: "right:425rpx;bottom:-23rpx;",
  1818. });
  1819. result.MilestoneFinished = _.orderBy(result.MilestoneFinished, ['DayNumber'], ["desc"]);
  1820. }
  1821. }
  1822. ctx.body = { errcode: 10000, result };
  1823. }
  1824. export async function GetShareWall(ctx) {
  1825. const param = {
  1826. UserID: ctx.query.UserID || 0,
  1827. PageIndex: ctx.query.PageIndex || 99999,
  1828. OrderType: "asc",
  1829. };
  1830. const result = {};
  1831. let list = await miaoguo.GetUserMiaoguoTaskByUserID(param);
  1832. if (list && list.length > 0) {
  1833. list = list.filter(item => item.IsShare == 1 || item.AphorismID > 0 || item.MilestoneDayNumber > 0);
  1834. result.Total = list.length;
  1835. result.Menu = [];
  1836. const len = Math.ceil(list.length / 100);
  1837. for (let i = len - 1; i >= 0; i--) {
  1838. const obj = {
  1839. PageIndex: i * 100,
  1840. Name: "第" + (i * 100 + 1) + "-" + (i * 100 + 100) + "次",
  1841. };
  1842. if (param.PageIndex == obj.PageIndex) {
  1843. obj.IsSelected = 1;
  1844. } else if (param.PageIndex == 99999 && i == len - 1) {
  1845. obj.IsSelected = 1;
  1846. param.PageIndex = i * 100;
  1847. }
  1848. result.Menu.push(obj);
  1849. }
  1850. let index = 0;
  1851. const mileStoneArr = constantClass.GetMilestone();
  1852. result.List = [];
  1853. for (let i = 0; i < list.length; i++) {
  1854. if (i >= param.PageIndex) {
  1855. const item = {
  1856. ID: list[i].ID,
  1857. CreateTime: moment(list[i].CreateTime).format("YYYY.MM.DD"),
  1858. HardworkingNumber: list[i].HardworkingNumber,
  1859. OrdinalPostfix: stringUtils.GetOrdinalPostfix(list[i].HardworkingNumber),
  1860. Color2: "#4D4D4D",
  1861. };
  1862. if (list[i].ImageID)
  1863. item.Image = "https://miaguo-1253256735.file.myqcloud.com/web/_share/qmjl_pic_e_" + list[i].ImageID + ".jpg";
  1864. if (list[i].MilestoneDayNumber > 0) {
  1865. for (const milestone of mileStoneArr) {
  1866. if (milestone.DayNumber == list[i].MilestoneDayNumber) {
  1867. item.Name = milestone.Name;
  1868. item.Color2 = milestone.Color1;
  1869. item.IsMilestone = 1;
  1870. break;
  1871. }
  1872. }
  1873. }
  1874. result.List.push(item);
  1875. if (++index == 100)
  1876. break;
  1877. }
  1878. }
  1879. result.List = _.orderBy(result.List, ['CreateTime'], ["desc"]);
  1880. if (result.List.length > 0) {
  1881. result.DateStart = moment(new Date(result.List[result.List.length - 1].CreateTime)).format("YYYY年MM月DD日");
  1882. result.DateEnd = moment(new Date(result.List[0].CreateTime)).format("YYYY年MM月DD日");
  1883. }
  1884. }
  1885. ctx.body = { errcode: 10000, result };
  1886. }
  1887. export async function GetShareItemInfo(ctx) {
  1888. const param = {
  1889. UserID: ctx.query.UserID || 0,
  1890. TaskID: ctx.query.TaskID || 0,
  1891. IsHistory: ctx.query.IsHistory || 0,
  1892. IsShow: ctx.query.IsShow || 0,
  1893. };
  1894. if (param.TaskID == -1) {
  1895. let isMilestone = 0;
  1896. let list = await miaoguo.GetUserMiaoguoTask(param);
  1897. if (list && list.length > 0) {
  1898. param.TaskID = list[0].ID;
  1899. if (list[0].MilestoneDayNumber > 0)
  1900. isMilestone = 1;
  1901. if (list[1]) {
  1902. const param2 = {
  1903. ID: list[0].ID,
  1904. DayNumber: list[1].DayNumber + 1,
  1905. };
  1906. if (list[1].HardworkingNumber > 0) {
  1907. param2.HardworkingNumber = list[1].HardworkingNumber + 1;
  1908. } else {
  1909. const param3 = {
  1910. UserID: param.UserID,
  1911. TaskID: -1,
  1912. IsGetHardworkingNumber: 1,
  1913. };
  1914. const hwn = await miaoguo.GetUserMiaoguoTask(param3);
  1915. if (hwn && hwn.length > 0)
  1916. param2.HardworkingNumber = hwn[0].HardworkingNumber + 1;
  1917. }
  1918. await miaoguo.UpdateMiaoguoTask(param2);
  1919. } else {
  1920. await miaoguo.UpdateMiaoguoTask({
  1921. ID: list[0].ID,
  1922. DayNumber: 1,
  1923. HardworkingNumber: 1,
  1924. });
  1925. }
  1926. }
  1927. if (!isMilestone) {
  1928. const maxAphorismID = config.MaxAphorismID;
  1929. const maxImageID = config.MaxImageID;
  1930. const list2 = await miaoguo.GetUserMiaoguoTaskByAphorismIDIsNotNull(param);
  1931. const aphorismID = [];
  1932. const imageID = [];
  1933. for (const item of list2) {
  1934. aphorismID.push(item.AphorismID);
  1935. imageID.push(item.ImageID);
  1936. }
  1937. const aArr = [];
  1938. for (let i = 1; i < maxAphorismID; i++) {
  1939. if (!aphorismID.includes(i))
  1940. aArr.push(i);
  1941. }
  1942. const aID = aArr.length > 0 ? aArr[stringUtils.Random(0, aArr.length - 1)] : stringUtils.Random(1, maxAphorismID);
  1943. const iArr = [];
  1944. for (let i = 1; i < maxImageID; i++) {
  1945. if (!imageID.includes(i))
  1946. iArr.push(i);
  1947. }
  1948. const iID = iArr.length > 0 ? iArr[stringUtils.Random(0, iArr.length - 1)] : stringUtils.Random(1, maxImageID);
  1949. list = await miaoguo.GetUserMiaoguoTask(param);
  1950. if (list && list[0] && list[0].AphorismID == 0) {
  1951. await miaoguo.UpdateMiaoguoTask({
  1952. UserID: param.UserID,
  1953. ID: list[0].ID,
  1954. AphorismID: aID,
  1955. ImageID: iID,
  1956. });
  1957. }
  1958. }
  1959. }
  1960. let result = {};
  1961. if (param.TaskID > 0) {
  1962. const info = await miaoguo.GetUserMiaoguoTask(param);
  1963. if (info && info.length > 0) {
  1964. result = info[0];
  1965. result.OrdinalPostfix = stringUtils.GetOrdinalPostfix(result.HardworkingNumber);
  1966. result.CreateTimeStr1 = moment(result.CreateTime).format("YYYY.MM.DD");
  1967. result.CreateTimeStr2 = moment(result.CreateTime).format("YYYY年MM月DD日");
  1968. if (!param.IsHistory || param.IsHistory == "0") {
  1969. param.CreateTime = moment(result.CreateTime).format("YYYY-MM-DD") + " 23:59:59";
  1970. const totalData1 = await miaoguo.GetUserMiaoguoPractiseCardTotal(param);
  1971. result.CardTaskTotal = totalData1[0].Count;
  1972. const totalData2 = await miaoguo.GetUserMiaoguoTaskTotal(param);
  1973. result.DurationTotal = Math.round((totalData2[0].DurationTotal || 0) / 60);
  1974. }
  1975. delete result.CreateTime;
  1976. if (result.MilestoneDayNumber) {
  1977. const mileStoneArr = constantClass.GetMilestone();
  1978. let indexMileStone = 0;
  1979. for (let i = 0; i < mileStoneArr.length; i++) {
  1980. if (result.MilestoneDayNumber == mileStoneArr[i].DayNumber) {
  1981. indexMileStone = i + 1;
  1982. result.Name = mileStoneArr[i].Name.split("\n");
  1983. result.Color = mileStoneArr[i].Color2;
  1984. break;
  1985. }
  1986. }
  1987. result.MilestoneImage = "https://miaguo-1253256735.file.myqcloud.com/web/_milestone/qmjl_milestone_material_" + indexMileStone + ".png";
  1988. const arrMilestone = await miaoguo.GetMilestone();
  1989. for (const milestone of arrMilestone) {
  1990. if (result.MilestoneID == milestone.ID) {
  1991. result.Content1 = result.CreateTimeStr2 + "是我家孩子用「秒过」做复习的第" + result.MilestoneDayNumber;
  1992. result.Content2 = "天。正在勇敢挑战" + result.CardTaskTotal + "个语数外知识点,专注复习超过";
  1993. result.Content3 = result.DurationTotal + "分钟。";
  1994. result.Content = [];
  1995. let str = milestone.Content;
  1996. do {
  1997. const str1 = str.substr(0, 28);
  1998. result.Content.push(str1);
  1999. str = str.substr(28);
  2000. } while (str != "");
  2001. delete result.AphorismID;
  2002. delete result.ImageID;
  2003. break;
  2004. }
  2005. }
  2006. delete result.MilestoneID;
  2007. } else {
  2008. const arrAphorism = await miaoguo.GetAphorism();
  2009. for (const aphorism of arrAphorism) {
  2010. if (result.AphorismID == aphorism.ID) {
  2011. result.Content = aphorism.Content;
  2012. result.Paraphrase = aphorism.Paraphrase;
  2013. result.AuthorCn = aphorism.AuthorCn;
  2014. result.AuthorEn = aphorism.AuthorEn;
  2015. result.Country = aphorism.Country;
  2016. result.Title = aphorism.Title;
  2017. result.BirthAndDeath = aphorism.BirthAndDeath;
  2018. result.Remark = aphorism.Remark;
  2019. result.AuthorImage = "https://miaguo-1253256735.file.myqcloud.com/web/_portrait/" + aphorism.Avatar;
  2020. result.WordImage = "https://miaguo-1253256735.file.myqcloud.com/web/_quotes/" + aphorism.WordImage;
  2021. delete result.MilestoneDayNumber;
  2022. delete result.MilestoneID;
  2023. break;
  2024. }
  2025. }
  2026. if (result.ImageID)
  2027. result.Image = "https://miaguo-1253256735.file.myqcloud.com/web/_share/qmjl_pic_b_" + result.ImageID + ".jpg";
  2028. delete result.ImageID;
  2029. }
  2030. result.DurationMinute = Math.round(result.Duration / 60);
  2031. if (result.DurationMinute == 0)
  2032. result.DurationMinute = 1;
  2033. result.ButtonString = param.IsShow > 0 ? "生成群接力图片" : "查看详细信息";
  2034. delete result.Flag;
  2035. }
  2036. }
  2037. ctx.body = { errcode: 10000, result };
  2038. }
  2039. export async function GetMiaoguoCardDeleteList2(ctx) {
  2040. const param = ctx.request.body || {};
  2041. param.UserID = ctx.query.UserID || "0";
  2042. if (param.PageID && param.PageID != "0" && param.Type == "web")
  2043. param.PageID = decryptWebID(param.PageID);
  2044. if (!param.PageCount)
  2045. param.PageCount = 20;
  2046. param.PageID = Number(param.PageID);
  2047. param.DataType = "delete";
  2048. const listID = await miaoguo.GetCardListID(param);
  2049. const ids = listID.map(item => item.MiaoguoCardID);
  2050. let list = [];
  2051. if (ids.length > 0) {
  2052. const rows = await miaoguo.GetCardList({
  2053. UserID: param.UserID,
  2054. IDArr: ids.join(','),
  2055. OrderType: "ac.UpdateTime desc,ac.CreateTime desc",
  2056. });
  2057. list = buildCardListFromRows(rows, [], true);
  2058. }
  2059. const result = {
  2060. Count: list.length,
  2061. List: [],
  2062. };
  2063. let index = 0;
  2064. let canPush = false;
  2065. for (const item of list) {
  2066. if (param.PageID == 0 || item.MiaoguoCardID == param.PageID)
  2067. canPush = true;
  2068. if (canPush) {
  2069. if (param.PageID == 0) {
  2070. result.List.push(item);
  2071. index++;
  2072. if (index >= param.PageCount)
  2073. break;
  2074. } else {
  2075. if (index > 0)
  2076. result.List.push(item);
  2077. index++;
  2078. if (index > param.PageCount)
  2079. break;
  2080. }
  2081. }
  2082. }
  2083. if (param.Type == "web") {
  2084. result.List = result.List.map(item => ({
  2085. ...item,
  2086. MiaoguoCardID: encryptWebID(item.MiaoguoCardID),
  2087. }));
  2088. }
  2089. ctx.body = { errcode: 10000, result };
  2090. }
  2091. export async function DeleteMiaoguoCardWeb(ctx) {
  2092. const param = ctx.request.body || {};
  2093. if (param.VisitType && decryptWebID(param.VisitType) == "web") {
  2094. if (stringUtils.IsArray(param.ID)) {
  2095. param.MiaoguoCardID = decryptWebIDList(param.ID).join(",");
  2096. param.IDArr = true;
  2097. } else {
  2098. param.MiaoguoCardID = decryptWebID(param.ID);
  2099. }
  2100. delete param.VisitType;
  2101. delete param.ID;
  2102. if (param.IsRecover == "true") {
  2103. param.Flag = 0;
  2104. delete param.IsRecover;
  2105. param.DeleteTime = null;
  2106. } else {
  2107. param.Flag = -1;
  2108. param.DeleteTime = moment().format("YYYY-MM-DD HH:mm:ss");
  2109. }
  2110. }
  2111. param.UserID = ctx.query.UserID;
  2112. param.UpdateTime = moment().format("YYYY-MM-DD HH:mm:ss");
  2113. await miaoguo.UpdateCard(param);
  2114. miaoguo.AddMiaoguoLog({
  2115. UserID: ctx.query.UserID,
  2116. LogID: String(param.MiaoguoCardID || '').indexOf(",") >= 0 ? 305 : 299,
  2117. Content: JSON.stringify(param),
  2118. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2119. globalCache.delete("GetMiaoguoCard###UserID=" + param.UserID);
  2120. ctx.body = { errcode: 10000 };
  2121. }
  2122. async function uploadMiaoguoFile(filename, filepath) {
  2123. const cos = new COS(config.QCloud);
  2124. const params = {
  2125. Bucket: 'miaguo-1253256735',
  2126. Region: 'ap-guangzhou',
  2127. Key: filename,
  2128. Body: fs.createReadStream(filepath),
  2129. ContentLength: fs.statSync(filepath).size,
  2130. };
  2131. return new Promise((resolve, reject) => {
  2132. cos.putObject(params, (err, data) => {
  2133. if (err) {
  2134. reject(err);
  2135. } else {
  2136. fsPromises.unlink(filepath).then(() => resolve(data)).catch(reject);
  2137. }
  2138. });
  2139. });
  2140. }
  2141. export async function ExportFileWeb(ctx) {
  2142. const param = ctx.request.body || {};
  2143. if (!param.ID || param.ID.length > 30) {
  2144. ctx.body = { errcode: 1004, errMsg: "上传数据过多!" };
  2145. return;
  2146. }
  2147. param.CardID = decryptWebIDList(param.ID).join(",");
  2148. delete param.ID;
  2149. await miaoguo.UpdateCardExportShare(param);
  2150. param.UserID = ctx.query.UserID;
  2151. const insertResult = await miaoguo.InsertExport(param);
  2152. const exportID = insertResult.insertId;
  2153. const content = encryptWebID(stringUtils.Random(10000, 99999) + "_" + exportID.toString());
  2154. const filename = param.Title + "_" + moment().format("YYYYMMDDHHmmss") + ".mg";
  2155. const dir = "./public/images/print";
  2156. await fsPromises.mkdir(dir, { recursive: true });
  2157. const filepath = path.join(dir, filename);
  2158. await fsPromises.writeFile(filepath, content);
  2159. await uploadMiaoguoFile(filename, filepath);
  2160. miaoguo.AddMiaoguoLog({
  2161. UserID: ctx.query.UserID,
  2162. LogID: 306,
  2163. Content: JSON.stringify({ ...param, ExportID: exportID }),
  2164. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2165. ctx.body = "https://miaguo-1253256735.file.myqcloud.com/" + decodeURI(filename);
  2166. }
  2167. async function importExportData(exportInfo, userIDTarget) {
  2168. const folderResult = await miaoguo.AddFolderInfo({
  2169. UserID: userIDTarget,
  2170. Name: exportInfo.Title,
  2171. ColorName: "#6B727C",
  2172. IconName: "cardlist_folder_white_66x60",
  2173. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  2174. ExportID: exportInfo.ID,
  2175. Flag: 0,
  2176. });
  2177. const folderID = folderResult.insertId;
  2178. const cardIDs = String(exportInfo.CardID || '').split(',').filter(Boolean);
  2179. for (const sourceCardID of cardIDs) {
  2180. const card = {
  2181. UserID: userIDTarget,
  2182. CardType: -1,
  2183. CreateSource: "web",
  2184. CreateTime: moment().format('YYYY-MM-DD HH:mm:ss'),
  2185. LimitTime: moment().format('YYYY-MM-DD HH:mm:ss'),
  2186. UpdateTime: moment().format('YYYY-MM-DD HH:mm:ss'),
  2187. SourceCardID: sourceCardID,
  2188. FolderID: folderID,
  2189. };
  2190. const insertInfo = await miaoguo.AddCard(card);
  2191. const listContent = await miaoguo.GetCardContentList(sourceCardID);
  2192. for (const content of listContent) {
  2193. await miaoguo.AddCardContent({
  2194. MiaoguoCardID: insertInfo.insertId,
  2195. ContentType: content.ContentType,
  2196. Content: content.Content,
  2197. });
  2198. }
  2199. }
  2200. }
  2201. export async function ImportFileWeb(ctx) {
  2202. const file = ctx.file;
  2203. const cleanup = async () => {
  2204. if (file && file.path && fs.existsSync(file.path))
  2205. await fsPromises.unlink(file.path);
  2206. };
  2207. if (!file || !file.originalname) {
  2208. ctx.body = { errcode: 1002, errMsg: "文件上传错误!" };
  2209. return;
  2210. }
  2211. if (file.originalname.indexOf(".mg") <= 0) {
  2212. await cleanup();
  2213. ctx.body = { errcode: 1001, errMsg: "格式不对!" };
  2214. return;
  2215. }
  2216. if (!ctx.query.UserID) {
  2217. await cleanup();
  2218. ctx.body = { errcode: 1002, errMsg: "文件上传错误!" };
  2219. return;
  2220. }
  2221. const userInfo = await miaoguo.GetUsersInfoByUserID({ UserID: ctx.query.UserID });
  2222. if (!userInfo || userInfo.length == 0 || moment(userInfo[0].ProductServiceTime).format("YYYYMMDDHHmmss") <= moment().format("YYYYMMDDHHmmss")) {
  2223. await cleanup();
  2224. ctx.body = { errcode: 1002, errMsg: "文件上传错误!" };
  2225. return;
  2226. }
  2227. try {
  2228. const data = await fsPromises.readFile(file.path);
  2229. const firstLine = data.toString().split("\n")[0];
  2230. let id = decryptWebID(firstLine);
  2231. id = id.substr(id.indexOf("_") + 1);
  2232. const exportInfo = await miaoguo.GetMiaoguoExport({ ID: id });
  2233. if (!exportInfo || exportInfo.length == 0)
  2234. throw new Error("export not found");
  2235. await importExportData(exportInfo[0], ctx.query.UserID);
  2236. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  2237. miaoguo.AddMiaoguoLog({
  2238. UserID: ctx.query.UserID,
  2239. LogID: 318,
  2240. Content: JSON.stringify(exportInfo[0]),
  2241. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2242. ctx.body = { errcode: 10000 };
  2243. } catch (err) {
  2244. ctx.body = { errcode: 1003, result: "文件内容错误!" };
  2245. } finally {
  2246. await cleanup();
  2247. }
  2248. }
  2249. export async function UpdateMiaoguoCardWebType(ctx) {
  2250. const param = ctx.request.body || {};
  2251. if (param.CardType) {
  2252. const cardIDs = decryptWebIDList(param.List);
  2253. const param2 = {
  2254. CardType: param.CardType,
  2255. UserID: ctx.query.UserID,
  2256. MiaoguoCardID: cardIDs.join(","),
  2257. };
  2258. await miaoguo.UpdateCardType(param2);
  2259. if (param.LimitTimeList) {
  2260. for (let i = 0; i < cardIDs.length; i++) {
  2261. await miaoguo.UpdateCard({
  2262. MiaoguoCardID: cardIDs[i],
  2263. LimitTime: param.LimitTimeList[i],
  2264. });
  2265. }
  2266. }
  2267. miaoguo.AddMiaoguoLog({
  2268. UserID: ctx.query.UserID,
  2269. LogID: param.LimitTimeList ? 307 : (param.CardType == -1 || param.CardType == "-1" ? 301 : 300),
  2270. Content: JSON.stringify({ ...param, List: cardIDs }),
  2271. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2272. }
  2273. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  2274. ctx.body = { errcode: 10000 };
  2275. }
  2276. export async function UpdateUserTagInfo(ctx) {
  2277. const param = ctx.request.body || {};
  2278. const param2 = {
  2279. UserID: ctx.query.UserID || 0,
  2280. };
  2281. if (param.CommonTags)
  2282. param2.CommonTags = param.CommonTags;
  2283. if (param.CommonSymbol)
  2284. param2.CommonSymbol = param.CommonSymbol;
  2285. await miaoguo.UpdateUsersByUserID(param2);
  2286. ctx.body = { errcode: 10000 };
  2287. }
  2288. export async function UpdateFolderInfoWeb(ctx) {
  2289. const param = ctx.request.body || {};
  2290. const folderParam = {
  2291. UserID: ctx.query.UserID || 0,
  2292. ID: param.FolderID || 0,
  2293. Name: param.Name || "",
  2294. Flag: param.Flag || 0,
  2295. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  2296. };
  2297. if (folderParam.ID > 0) {
  2298. if (folderParam.Flag == -1)
  2299. delete folderParam.Name;
  2300. await miaoguo.UpdateFolderInfo(folderParam);
  2301. if (folderParam.Flag == -1) {
  2302. await miaoguo.UpdateFolderList({
  2303. UserID: folderParam.UserID,
  2304. FolderOldID: folderParam.ID,
  2305. FolderID: 0,
  2306. });
  2307. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  2308. }
  2309. miaoguo.AddMiaoguoLog({
  2310. UserID: ctx.query.UserID,
  2311. LogID: 310,
  2312. Content: JSON.stringify(folderParam),
  2313. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2314. } else {
  2315. delete folderParam.ID;
  2316. await miaoguo.AddFolderInfo(folderParam);
  2317. miaoguo.AddMiaoguoLog({
  2318. UserID: ctx.query.UserID,
  2319. LogID: 309,
  2320. Content: JSON.stringify(folderParam),
  2321. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2322. }
  2323. globalCache.delete("GetUserFolderList?UserID=" + folderParam.UserID);
  2324. ctx.body = { errcode: 10000 };
  2325. }
  2326. export async function UpdateFolderList(ctx) {
  2327. const param = ctx.request.body || {};
  2328. param.UserID = ctx.query.UserID || 0;
  2329. param.CardID = decryptWebIDList(param.CardID).join(",");
  2330. const updateTimeParam = {
  2331. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  2332. UserID: param.UserID,
  2333. };
  2334. if (param.FolderID > 0) {
  2335. updateTimeParam.ID = param.FolderID;
  2336. await miaoguo.UpdateFolderInfo(updateTimeParam);
  2337. } else {
  2338. updateTimeParam.CardID = param.CardID;
  2339. await miaoguo.UpdateFolderInfo(updateTimeParam);
  2340. }
  2341. if (!param.FolderID)
  2342. param.FolderID = 0;
  2343. await miaoguo.UpdateFolderList(param);
  2344. globalCache.delete("GetUserFolderList?UserID=" + param.UserID);
  2345. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  2346. miaoguo.AddMiaoguoLog({
  2347. UserID: ctx.query.UserID,
  2348. LogID: 303,
  2349. Content: JSON.stringify(param),
  2350. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2351. ctx.body = { errcode: 10000 };
  2352. }
  2353. export async function UpdateCollectList(ctx) {
  2354. const param = ctx.request.body || {};
  2355. param.UserID = ctx.query.UserID || 0;
  2356. param.CardID = decryptWebIDList(param.CardID).join(",");
  2357. if (!param.IsCollect)
  2358. param.IsCollect = 0;
  2359. await miaoguo.UpdateCollectList(param);
  2360. globalCache.delete("GetUserFolderList?UserID=" + param.UserID);
  2361. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  2362. miaoguo.AddMiaoguoLog({
  2363. UserID: ctx.query.UserID,
  2364. LogID: 302,
  2365. Content: JSON.stringify(param),
  2366. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2367. ctx.body = { errcode: 10000 };
  2368. }
  2369. export async function ResetUserCard(ctx) {
  2370. const param = ctx.request.body || {};
  2371. if (param.List) {
  2372. const cardIDs = decryptWebIDList(param.List);
  2373. await miaoguo.ResetCard({
  2374. UserID: ctx.query.UserID,
  2375. MiaoguoCardID: cardIDs.join(","),
  2376. CardType: param.CardType,
  2377. });
  2378. miaoguo.AddMiaoguoLog({
  2379. UserID: ctx.query.UserID,
  2380. LogID: 371,
  2381. Content: JSON.stringify({ ...param, List: cardIDs }),
  2382. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2383. }
  2384. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  2385. ctx.body = { errcode: 10000 };
  2386. }
  2387. export async function ImportExcelData(ctx) {
  2388. const param = ctx.request.body || {};
  2389. param.UserID = ctx.query.UserID || "0";
  2390. const folder = {
  2391. UserID: param.UserID,
  2392. Name: "D" + moment().format("HHmmss_YYYYMMDD"),
  2393. ColorName: "#6B727C",
  2394. IconName: "cardlist_folder_white_66x60",
  2395. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  2396. ExportID: 0,
  2397. Flag: 0,
  2398. };
  2399. const insertFolder = await miaoguo.AddFolderInfo(folder);
  2400. const list = JSON.parse(param.DataList);
  2401. for (const item of list) {
  2402. const time = moment().format('YYYY-MM-DD HH:mm:ss');
  2403. const insertInfo = await miaoguo.AddCard({
  2404. UserID: param.UserID,
  2405. CardType: -1,
  2406. CreateSource: "web",
  2407. CreateTime: time,
  2408. LimitTime: time,
  2409. UpdateTime: time,
  2410. FolderID: insertFolder.insertId,
  2411. });
  2412. const cardID = insertInfo.insertId;
  2413. await miaoguo.AddCardContent({ MiaoguoCardID: cardID, ContentType: 0, Content: "" });
  2414. await miaoguo.AddCardContent({
  2415. MiaoguoCardID: cardID,
  2416. ContentType: 1,
  2417. Content: stringUtils.ReplaceAllString(item.Question || "", "'", "ˈ"),
  2418. });
  2419. await miaoguo.AddCardContent({
  2420. MiaoguoCardID: cardID,
  2421. ContentType: 2,
  2422. Content: stringUtils.ReplaceAllString(item.Answer || "", "'", "ˈ"),
  2423. });
  2424. await miaoguo.AddCardContent({ MiaoguoCardID: cardID, ContentType: 3, Content: "" });
  2425. }
  2426. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  2427. miaoguo.AddMiaoguoLog({
  2428. UserID: ctx.query.UserID,
  2429. LogID: 453,
  2430. Content: JSON.stringify(param.List),
  2431. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  2432. ctx.body = { errcode: 10000 };
  2433. }
  2434. export async function GetAccreditInfo2(ctx) {
  2435. const param = {
  2436. Type: ctx.query.Type || 0,
  2437. };
  2438. const result = {};
  2439. result.Remind = "为安全运作,本功能不能没有用户名字和头像。请补全资料,并尽量与您的微信名字和头像保持一致。点击项目可直接填充微信信息。";
  2440. if (param.Type == 0) {
  2441. result.Text1 = "编辑用户信息";
  2442. result.Text2 = "";
  2443. result.Text3 = "您的头像、名字请尽量与微信保持一致,这对一部分功能的安全运行非常重要。同时,这也大大方便客服为您处理新手班、排除故障、帐户、活动奖励发放等需要微信联系的重要事宜。";
  2444. result.Text4 = "返回";
  2445. } else if (param.Type == "binding") {
  2446. result.Text1 = "请先核对用户信息";
  2447. result.Text2 = "使用「帐号绑定」之前,请先核对头像、名字是否与您的微信保持一致,这对本功能安全运行非常重要。这也大大方便客服在您遇到故障或数据问题时在微信中联络到您,即时了解情况,减少您的损失。\n\n如信息不一致,点击对应项目即可直接填充微信头像或名字,无须手动输入。确认后点「下一步」继续。";
  2448. result.Text3 = "";
  2449. result.Text4 = "下一步";
  2450. } else if (param.Type == "family") {
  2451. result.Text1 = "请先核对用户信息";
  2452. result.Text2 = "使用「家庭用户」之前,请先核对头像、名字是否与您的微信保持一致,这对本功能安全运行非常重要。这也大大方便客服在您遇到故障或数据问题时在微信中联络到您,即时了解情况,减少您的损失。\n\n如信息不一致,点击对应项目即可直接填充微信头像或名字,无须手动输入。确认后点「下一步」继续。";
  2453. result.Text3 = "";
  2454. result.Text4 = "下一步";
  2455. }
  2456. ctx.body = { errcode: 10000, result };
  2457. }
  2458. export async function GetAccreditInfo(ctx) {
  2459. const param = {
  2460. Type: ctx.query.Type || 0,
  2461. };
  2462. const result = {};
  2463. if (param.Type == 0) {
  2464. result.Text1 = "请允许秒过";
  2465. result.Text2 = "获得您的头像、昵称\n用于注册登录";
  2466. result.Text3 = "非常抱歉,不填写名字会造成后续服务无法进行,如要继续使用秒过,烦劳提供名字。由于微信隐私保护,秒过不能直接获得您的信息,这会造成培训、客服、统计、社群、官网等服务无法运行。此信息仅用于秒过,请放心。";
  2467. } else {
  2468. result.Text1 = "用微信注册秒过";
  2469. result.Text2 = "请允许秒过\n获得您的微信头像、昵称";
  2470. result.Text3 = "非常抱歉,不填写名字会造成后续服务无法进行,如要继续使用秒过,烦劳提供名字。由于微信隐私保护,秒过不能直接获得您的信息,这会造成培训、客服、统计、社群、官网等服务无法运行。此信息仅用于秒过,请放心。";
  2471. }
  2472. result.Btn1 = "允许";
  2473. result.Btn2 = "忽略";
  2474. ctx.body = { errcode: 10000, result };
  2475. }
  2476. export async function GetMiaoguoLesson2(ctx) {
  2477. const arr = constantClass.getLessonList();
  2478. ctx.body = { errcode: 10000, result: arr };
  2479. }
  2480. export async function GetMiaoguoNewUserImageList(ctx) {
  2481. const result = constantClass.getNewUserImageList();
  2482. ctx.body = { errcode: 10000, result };
  2483. }
  2484. export async function GetMiaoguoNewUserImageList3126(ctx) {
  2485. const result = constantClass.getNewUserImageList3136();
  2486. ctx.body = { errcode: 10000, result };
  2487. }
  2488. export async function GetMiaoguoTipsList(ctx) {
  2489. const param = {
  2490. Version: ctx.query.version || "?v=8",
  2491. };
  2492. const result = constantClass.GetMiaoguoTipsList();
  2493. for (const section of result) {
  2494. for (const item of section.List) {
  2495. item.Cover = item.Cover + param.Version;
  2496. for (let i = 0; i < item.Images.length; i++)
  2497. item.Images[i] = item.Images[i] + param.Version;
  2498. }
  2499. }
  2500. ctx.body = { errcode: 10000, result };
  2501. }
  2502. export async function UpdateUserNickNameAndAvatar(ctx) {
  2503. const param = ctx.request.body;
  2504. param.UserID = ctx.query.UserID;
  2505. if (!param.NickName)
  2506. param.NickName = "陌生用户";
  2507. await miaoguo.UpdateUsersByUserID(param);
  2508. const log = {
  2509. UserID: param.UserID,
  2510. LogID: 315,
  2511. Content: JSON.stringify(param),
  2512. };
  2513. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  2514. ctx.body = { errcode: 10000 };
  2515. }
  2516. export async function UpdateUserMemberInfoPost(ctx) {
  2517. const param = ctx.request.body;
  2518. param.UserID = ctx.query.UserID || "0";
  2519. param.FieldType = ctx.query.FieldType || 0;
  2520. if (param.FieldType == 1)
  2521. param.NickNameRemark = param.Content;
  2522. else if (param.FieldType == 2)
  2523. param.IsMessage = param.Content;
  2524. else if (param.FieldType == 3)
  2525. param.MessageTime = param.Content;
  2526. if (param.FieldType) {
  2527. delete param.Content;
  2528. delete param.FieldType;
  2529. await miaoguo.UpdateUsersByUserID(param);
  2530. }
  2531. ctx.body = { errcode: 10000 };
  2532. }
  2533. export async function UpdateUserMemberInfo(ctx) {
  2534. const param = {
  2535. UserID: ctx.query.UserID || 0,
  2536. WSUUserID: ctx.query.WSUUserID || 0,
  2537. IsTryOut: ctx.query.IsTryOut,
  2538. IsMember: ctx.query.IsMember,
  2539. ClassID: ctx.query.ClassID || 0,
  2540. GroupID: ctx.query.GroupID || 3,
  2541. NickNameRemark: ctx.query.NickNameRemark,
  2542. };
  2543. let canUpdate = true;
  2544. if (ctx.query.NickNameRemark && ctx.query.UserID) {
  2545. delete param.WSUUserID;
  2546. delete param.IsMember;
  2547. delete param.ClassID;
  2548. delete param.GroupID;
  2549. delete param.IsTryOut;
  2550. await miaoguo.UpdateUsersByUserID(param);
  2551. } else if (ctx.query.GroupID && ctx.query.UserID) {
  2552. delete param.WSUUserID;
  2553. delete param.IsMember;
  2554. delete param.ClassID;
  2555. delete param.NickNameRemark;
  2556. delete param.IsTryOut;
  2557. await miaoguo.UpdateUsersByUserID(param);
  2558. } else if (ctx.query.IsTryOut && ctx.query.UserID) {
  2559. delete param.WSUUserID;
  2560. delete param.IsMember;
  2561. delete param.GroupID;
  2562. delete param.NickNameRemark;
  2563. if (param.IsTryOut == -1)
  2564. param.IsTryOut = 0;
  2565. const userInfo = await miaoguo.GetUsersInfoByUserID(param);
  2566. param.ClassID = userInfo[0].ClassID;
  2567. const classList = await miaoguo.GetClassList(param);
  2568. param.ProductServiceTime = moment(classList[0].StartDate).add(classList[0].DayNumber - 1, 'days').format('YYYY-MM-DD 23:59:59');
  2569. delete param.Sort;
  2570. await miaoguo.UpdateUsersByUserID(param);
  2571. } else if (ctx.query.ParentUserID) {
  2572. delete param.GroupID;
  2573. delete param.IsMember;
  2574. delete param.ClassID;
  2575. delete param.NickNameRemark;
  2576. delete param.IsTryOut;
  2577. param.ParentUserID = ctx.query.ParentUserID;
  2578. if (param.UserID != param.ParentUserID) {
  2579. const users = await miaoguo.GetUserBindingList({ UserID: ctx.query.ParentUserID || 0 });
  2580. if (users && users.length > 0)
  2581. canUpdate = false;
  2582. if (canUpdate) {
  2583. delete param.WSUUserID;
  2584. await miaoguo.UpdateUsersByUserID(param);
  2585. }
  2586. }
  2587. } else {
  2588. delete param.NickNameRemark;
  2589. delete param.IsTryOut;
  2590. if (param.IsMember === undefined)
  2591. delete param.IsMember;
  2592. delete param.WSUUserID;
  2593. await miaoguo.UpdateUsersByUserID(param);
  2594. }
  2595. const log = {
  2596. UserID: ctx.query.UserID,
  2597. LogID: 315,
  2598. Content: JSON.stringify(param),
  2599. };
  2600. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  2601. ctx.body = { errcode: 10000 };
  2602. }
  2603. export async function GetUserBindingList(ctx) {
  2604. const param = {
  2605. UserID: ctx.query.UserID || 0,
  2606. };
  2607. const userInfo = await miaoguo.GetUserBindingList(param);
  2608. ctx.body = { errcode: 10000, result: userInfo };
  2609. }
  2610. export async function UpdateUserBinding(ctx) {
  2611. const param = {
  2612. ParentUserID: ctx.query.ParentUserID || 0,
  2613. };
  2614. await miaoguo.UpdateUserBinding(param);
  2615. const log = {
  2616. UserID: ctx.query.ParentUserID,
  2617. LogID: 319,
  2618. Content: JSON.stringify(param),
  2619. };
  2620. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  2621. ctx.body = { errcode: 10000 };
  2622. }
  2623. export async function GetMiaoguoWXUserInfo(ctx) {
  2624. const param = {
  2625. UserID: ctx.query.UserID || 0,
  2626. FieldStr: ctx.query.FieldStr,
  2627. GetWechatUserID: ctx.query.GetWechatUserID || 0,
  2628. };
  2629. const userInfo = await miaoguo.GetUsersInfoByUserID(param);
  2630. if (param.GetWechatUserID == 1) {
  2631. const wechatUserInfo = await miaoguo.GetWechatServiceInfoByMGUserID(param);
  2632. if (wechatUserInfo && wechatUserInfo.length > 0)
  2633. userInfo[0].WechatUserID = wechatUserInfo[0].WechatUserID;
  2634. }
  2635. const item = userInfo[0];
  2636. if (item && item.IsPromoter > 0) {
  2637. const a = moment(item.PromotionLimitTime);
  2638. const b = moment();
  2639. item.PromotionLimitDays = a.diff(b, 'days');
  2640. item.PromotionLimitTime = moment(item.PromotionLimitTime).format('YYYY.MM.DD');
  2641. item.WXServiceCharge = config.WXServiceCharge;
  2642. item.IndividualIncomeTax = config.IndividualIncomeTax;
  2643. if (!item.PayeeWechatAccount)
  2644. item.PayeeWechatAccount = item.NickName;
  2645. if (!item.WXAccount)
  2646. item.WXAccount = item.NickName;
  2647. if (!item.RealName)
  2648. item.RealName = "";
  2649. if (item.Introducer) {
  2650. const userInfo2 = await miaoguo.GetUsersInfoByUserID({ UserID: item.Introducer, FieldStr: "NickName,AvatarUrl" });
  2651. item.IntroducerNickName = userInfo2[0].NickName;
  2652. item.IntroducerAvatarUrl = userInfo2[0].AvatarUrl;
  2653. }
  2654. }
  2655. if (item) {
  2656. delete item.OpenID;
  2657. delete item.UnionID;
  2658. delete item.Flag;
  2659. }
  2660. ctx.body = { errcode: 10000, result: item };
  2661. }
  2662. export async function GetUserCardCount(ctx) {
  2663. const param = {
  2664. UserID: ctx.query.UserID || 0,
  2665. CardType: ctx.query.CardType || 2,
  2666. IsCollect: ctx.query.IsCollect || 0,
  2667. OrderType: ctx.query.OrderType || 0,
  2668. HasHardworking: ctx.query.HasHardworking || 0,
  2669. };
  2670. if (param.UserID == "undefined")
  2671. param.UserID = 0;
  2672. if (param.CardType == "undefined")
  2673. param.CardType = 2;
  2674. if (param.IsCollect == "undefined")
  2675. param.IsCollect = 0;
  2676. const cardNumber = await miaoguo.GetCardCount(param);
  2677. const result = {
  2678. CardNumber: cardNumber[0].count,
  2679. };
  2680. if (param.CardType == 2) {
  2681. param.CardType = 0;
  2682. const cardNormalNumber = await miaoguo.GetCardCount(param);
  2683. result.NormalNumber = cardNormalNumber[0].count;
  2684. param.CardType = 1;
  2685. const cardUrgentNumber = await miaoguo.GetCardCount(param);
  2686. result.UrgentNumber = cardUrgentNumber[0].count;
  2687. if (param.OrderType == 5 || param.OrderType == 6)
  2688. result.CardNumber = result.NormalNumber + result.UrgentNumber;
  2689. }
  2690. if (param.HasHardworking == 1) {
  2691. const param2 = {
  2692. FieldStr: "ID,CreateTime,IsShare,HardworkingNumber,MilestoneDayNumber,MilestoneID,AphorismID,ImageID",
  2693. UserID: param.UserID,
  2694. OrderType: " desc limit 1",
  2695. WhereStr: " and (MilestoneDayNumber>0 or AphorismID>0) ",
  2696. };
  2697. const list = await miaoguo.GetUserMiaoguoTaskByUserID(param2);
  2698. if (list && list.length > 0) {
  2699. const item = list[0];
  2700. delete item.CreateTime;
  2701. item.OrdinalPostfix = stringUtils.GetOrdinalPostfix(item.HardworkingNumber);
  2702. if (item.MilestoneDayNumber > 0) {
  2703. const mileStoneArr = constantClass.GetMilestone();
  2704. for (const milestone of mileStoneArr) {
  2705. if (milestone.DayNumber == item.MilestoneDayNumber) {
  2706. item.Name = milestone.Name;
  2707. item.BgColor = milestone.Color2;
  2708. item.IsMilestone = 1;
  2709. break;
  2710. }
  2711. }
  2712. } else {
  2713. item.Image = "https://miaguo-1253256735.file.myqcloud.com/web/_share/qmjl_pic_d_" + item.ImageID + ".jpg";
  2714. }
  2715. result.Hardworking = item;
  2716. }
  2717. }
  2718. ctx.body = { errcode: 10000, result };
  2719. }
  2720. export async function GetMiaoguoLearningRecord(ctx) {
  2721. const param = {
  2722. CardID: ctx.query.CardID || 0,
  2723. };
  2724. const list = await miaoguo.GetMiaoguoLearningRecord(param);
  2725. let total = 0;
  2726. for (const item of list) {
  2727. item.CreateTime = moment(item.CreateTime).format('YYYY年MM月DD日 HH:mm');
  2728. total += item.Duration;
  2729. item.Duration = stringUtils.getMinuteSecond(item.Duration, true, true);
  2730. if (item.Duration && item.Duration.indexOf("分") > 0)
  2731. item.Duration = item.Duration.replace("分", "分钟");
  2732. }
  2733. const result = {};
  2734. result.Total = stringUtils.getMinuteSecond(total, true, true);
  2735. if (result.Total && result.Total.indexOf("分") > 0)
  2736. result.Total = result.Total.replace("分", "分钟");
  2737. result.List = list;
  2738. ctx.body = { errcode: 10000, result };
  2739. }
  2740. export async function SendMiaoguoCoupon(ctx) {
  2741. const param = {
  2742. UserID: Number(ctx.query.UserID) || 0,
  2743. CouponType: ctx.query.CouponType,
  2744. LimitTime: ctx.query.LimitTime,
  2745. IsUse: 0,
  2746. Flag: 0,
  2747. };
  2748. param.LimitTime = param.LimitTime.replace("年", "-");
  2749. param.LimitTime = param.LimitTime.replace("月", "-");
  2750. param.LimitTime = param.LimitTime.replace("日", "");
  2751. param.LimitTime = moment(param.LimitTime).format("YYYY-MM-DD 23:59:59");
  2752. const list = await miaoguo.GetMiaoguoCoupon(param);
  2753. if (!list || list.length == 0) {
  2754. if (param.CouponType == 134)
  2755. param.CouponNumber = 20;
  2756. await miaoguo.AddMiaoguoCoupon(param);
  2757. }
  2758. ctx.body = { errcode: 10000 };
  2759. }
  2760. export async function UseMiaoguoCoupon(ctx) {
  2761. const param = {
  2762. UserID: ctx.query.UserID || 0,
  2763. CouponType: ctx.query.CouponType || 0,
  2764. CouponNumber: ctx.query.CouponNumber || 0,
  2765. IsUse: 1,
  2766. };
  2767. await miaoguo.UpdateMiaoguoCoupon(param);
  2768. param.CouponNumber = parseInt(param.CouponNumber);
  2769. if (param.CouponNumber > 0) {
  2770. const userList = await miaoguo.GetUsersInfoByUserID(param);
  2771. if (userList && userList.length > 0) {
  2772. const param2 = {
  2773. UserID: param.UserID,
  2774. ProductServiceTime: moment(userList[0].ProductServiceTime).add(param.CouponNumber, "days").format('YYYY-MM-DD 23:59:59'),
  2775. };
  2776. await miaoguo.UpdateUsersByUserID(param2);
  2777. }
  2778. }
  2779. ctx.body = { errcode: 10000 };
  2780. }
  2781. export async function ShareMiaoguoTask(ctx) {
  2782. const param = {
  2783. UserID: ctx.query.UserID || 0,
  2784. CreateTime: ctx.query.CreateTime + " 00:00:00",
  2785. IsShare: 1,
  2786. };
  2787. if (!param.CreateTime || param.CreateTime == "Invalid date 00:00:00" || param.CreateTime < moment().add(-7, "days").format('YYYY-MM-DD 00:00:00'))
  2788. param.CreateTime = moment().format('YYYY-MM-DD 00:00:00');
  2789. await miaoguo.UpdateMiaoguoTask(param);
  2790. ctx.body = { errcode: 10000 };
  2791. }
  2792. export async function GetIntroducerUserList(ctx) {
  2793. const param = {
  2794. UserID: ctx.query.UserID || 0,
  2795. };
  2796. param.type = "web";
  2797. const list0 = await miaoguo.GetIntroducerUserList(param);
  2798. for (const item of list0) {
  2799. if (item.NickName && item.NickName.length > 5)
  2800. item.NickName = item.NickName.substr(0, 5) + "...";
  2801. else if (item.NickName === "陌生用户")
  2802. item.NickName = "用户未授权";
  2803. if (item.NickName == "用户未授权" && item.NickName != "陌生用户") {
  2804. item.AvatarUrl = item.AvatarUrl2;
  2805. item.NickName = item.NickName2;
  2806. }
  2807. if (item.AvatarUrl === "../images/universalpic_face_default_blue_120x120.png")
  2808. item.AvatarUrl = "../images/universalpic_face_default_gray_114x114.png";
  2809. else if (item.AvatarUrl && item.AvatarUrl.indexOf("/132"))
  2810. item.AvatarUrl = item.AvatarUrl.replace("/132", "/46");
  2811. }
  2812. delete param.type;
  2813. const list = await miaoguo.GetIntroducerUserList(param);
  2814. for (const item of list) {
  2815. if (item.NickName && item.NickName.length > 5)
  2816. item.NickName = item.NickName.substr(0, 5) + "...";
  2817. else if (item.NickName === "陌生用户")
  2818. item.NickName = "用户未授权";
  2819. if (item.AvatarUrl === "../images/universalpic_face_default_blue_120x120.png")
  2820. item.AvatarUrl = "../images/universalpic_face_default_gray_114x114.png";
  2821. else if (item.AvatarUrl && item.AvatarUrl.indexOf("/132"))
  2822. item.AvatarUrl = item.AvatarUrl.replace("/132", "/46");
  2823. item.ProductServiceTime = moment(item.ProductServiceTime).format('YYYY-MM-DD HH:mm:ss');
  2824. if (item.PayTime)
  2825. item.PayTime = moment(item.PayTime).format('YYYY-MM-DD HH:mm:ss');
  2826. else
  2827. item.PayTime = "1900-01-01 00:00:00";
  2828. }
  2829. ctx.body = { errcode: 10000, result: { List1: list0, List2: list } };
  2830. }
  2831. export async function GetMiaoguoShareInfo220(ctx) {
  2832. const param = {
  2833. ProgramID: 166,
  2834. Version: ctx.query.Version,
  2835. };
  2836. const result = [{}];
  2837. result[0].Text1 = "推广物料";
  2838. result[0].Text2 = "转发小程序";
  2839. result[0].Text3 = "成效最佳的方法\n转发含有推广码的小程序到群或朋友";
  2840. result[0].BtnStr = "转发";
  2841. result[0].OpenType = "share";
  2842. result[0].BindTap = "";
  2843. const versionList = await commonModel.GetProductVersionList(param);
  2844. if (versionList && versionList[0] && versionList[0].IsShowPay > 0) {
  2845. result.push({});
  2846. result[1].Text1 = "推广物料";
  2847. result[1].Text2 = "勤勉接力图片";
  2848. result[1].Text3 = "接力图片自带推广二维码\n图片内容丰富,形式生动,非常适合朋友圈\n一方面让关心您的朋友一起见证孩子的努力\n另一方面潜移默化顺带推广";
  2849. result[1].Image = "promotion_materials_qmjl.png?0";
  2850. result[1].BtnStr = "前往勤勉接力栏目生成图片";
  2851. result[1].BindTap = "gotoShareIndex";
  2852. result.push({});
  2853. result[2].Text1 = "推广物料";
  2854. result[2].Text2 = "海报图片";
  2855. result[2].Text3 = "点击效果图,生成含有推广码的海报\n长按图片保存到手机相册";
  2856. result[2].Images = [{ icon: "promotion_materials_hb01.png?0", id: "1" }, { icon: "promotion_materials_hb04.png?0", id: "4" }, { icon: "promotion_materials_hb02.png?0", id: "2" }, { icon: "promotion_materials_hb03.png?0", id: "3" }];
  2857. result.push({});
  2858. result[3].Text1 = "推广物料";
  2859. result[3].Text2 = "配图文案";
  2860. result[3].Text3 = "复制秒过介绍文案\n可自行编辑加工(内含网页链接)";
  2861. result[3].BtnStr = "复制文案";
  2862. result[3].OpenType = "";
  2863. result[3].BindTap = "buildText";
  2864. result.push({});
  2865. result[4].Text1 = "推广物料";
  2866. result[4].Text2 = "网页链接";
  2867. result[4].Text3 = "发布含有推广码的网页\n点击「复制链接」,然后粘贴到文字中";
  2868. result[4].BtnStr = "复制链接";
  2869. result[4].OpenType = "";
  2870. result[4].BindTap = "buildUrl";
  2871. }
  2872. ctx.body = { errcode: 10000, result };
  2873. }
  2874. export async function GetMiaoguoShareInfo(ctx) {
  2875. const param = {
  2876. ProgramID: 166,
  2877. Version: ctx.query.Version,
  2878. };
  2879. const result = [{}];
  2880. result[0].Text2 = "转发小程序";
  2881. result[0].Text3 = "转发含有推荐人信息的小程序";
  2882. result[0].BtnStr = "转发";
  2883. result[0].OpenType = "share";
  2884. result[0].BindTap = "";
  2885. const versionList = await commonModel.GetProductVersionList(param);
  2886. if (versionList && versionList[0] && versionList[0].IsShowPay > 0) {
  2887. result[0].Text1 = "方法一";
  2888. result.push({});
  2889. result[1].Text1 = "方法二";
  2890. result[1].Text2 = "发到朋友圈";
  2891. result[1].Text3 = "发布含有推荐人信息的图片";
  2892. result[1].Text4 = "存图步骤";
  2893. result[1].Text5 = "点击生成图片\n长按图片保存到手机相册";
  2894. result[1].BtnStr = "生成专门图片";
  2895. result[1].OpenType = "";
  2896. result[1].BindTap = "buildImage";
  2897. }
  2898. ctx.body = { errcode: 10000, result };
  2899. }
  2900. export async function BuildData(ctx) {
  2901. const param = {
  2902. Type: ctx.query.Type || 1,
  2903. TypeDetail: ctx.query.TypeDetail || 1,
  2904. };
  2905. const result = [];
  2906. const sleep = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
  2907. const sendTemplateMessage = async (userID, templateID, paramStr, useProduction = false) => {
  2908. const baseURL = useProduction ? 'https://www.kylx365.com' : 'http://localhost:' + config.port;
  2909. await axios.get(baseURL + '/api/SendWXServiceTemplateMessage', {
  2910. params: { UserID: userID, TemplateID: templateID, ...(paramStr ? { ParamStr: paramStr } : {}) },
  2911. });
  2912. };
  2913. if (param.Type == 1) {
  2914. const sql = "select u.UserID,u.NickName,count(distinct DATE_FORMAT(r.CreateTime,'%Y%m%d')) as '天数',sum(r.Duration)/60 as '使用分钟数' " +
  2915. "from MiaoguoWXUsers u " +
  2916. "inner join MiaoguoCard c on u.UserID=c.UserID " +
  2917. "inner join MiaoguoLearningRecord r on c.MiaoguoCardID = r.CardID " +
  2918. "where r.Flag=0 and r.CreateTime>='2019-07-01' and r.CreateTime<'2019-09-01' " +
  2919. "group by u.UserID having 天数>=5 order by 天数 desc;";
  2920. const list = await miaoguo.RunSql([], sql);
  2921. const limitTime = moment().add(6, "months").format("YYYY-MM-DD");
  2922. for (const item of list) {
  2923. let day = 0;
  2924. if (item.天数 >= 50) day = 30;
  2925. else if (item.天数 >= 40) day = 20;
  2926. else if (item.天数 >= 30) day = 15;
  2927. else if (item.天数 >= 20) day = 10;
  2928. else if (item.天数 >= 10) day = 5;
  2929. else if (item.天数 >= 5) day = 2;
  2930. const insertSql = "insert into MiaoguoCoupon (CouponType,UserID,LimitTime,IsUse,CouponNumber) values (136," + item.UserID + ",'" + limitTime + "',0," + day + ");";
  2931. result.push(insertSql);
  2932. }
  2933. } else if (param.Type == 2) {
  2934. const list = await miaoguo.RunSql([], 'select UserID from MiaoguoWXUsers where IsPay=1 and ClassID=4;');
  2935. for (const item of list) {
  2936. const response = await axios.get('http://localhost:' + config.port + '/api/GetMiaoguoCheckList', {
  2937. params: { UserID: item.UserID },
  2938. }).then(res => res.data);
  2939. const checklist = response.result;
  2940. let day = 0;
  2941. for (let i = 1; i < checklist.List.length; i++) {
  2942. if (checklist.List[i].Max <= checklist.List[i].Finished) day += 2;
  2943. }
  2944. await miaoguo.RunSql([
  2945. checklist.UserID,
  2946. moment().add(6, 'months').format('YYYY-MM-DD'),
  2947. day,
  2948. ], 'insert into MiaoguoCoupon (CouponType,UserID,LimitTime,IsUse,CouponNumber) values (134,?,?,0,?);');
  2949. await sleep(500);
  2950. }
  2951. } else if (param.Type == 3) {
  2952. const sql = "select w.UserID from MiaoguoWXUsers u inner join WechatServiceWXUsers w on u.UnionID=w.UnionID " +
  2953. "where u.IsPay=1 and u.UserID not in (" +
  2954. "select w.Introducer from WechatServiceWXUsers w inner join MiaoguoWXUsers u on w.Introducer=u.UserID " +
  2955. "where u.IsPay=1 and u.UserID>7 group by w.Introducer) union all " +
  2956. "select w2.UserID from WechatServiceWXUsers w inner join MiaoguoWXUsers u on w.Introducer=u.UserID " +
  2957. "inner join WechatServiceWXUsers w2 on w.UnionID=w2.UnionID " +
  2958. "where u.IsPay=1 and u.UserID>7 group by w.Introducer having count(*)<3;";
  2959. const list = await miaoguo.RunSql([], sql);
  2960. const userIDs = _.uniq(list.map(item => item.UserID).sort());
  2961. for (const userID of userIDs) {
  2962. if (userID === 1) await sendTemplateMessage(userID, 'PopularizationPlan');
  2963. await sleep(500);
  2964. }
  2965. } else if (param.Type == 4) {
  2966. let sql = "select wu.UserID,u.NickName,count(distinct DATE_FORMAT(r.CreateTime,'%Y%m%d')) as 'Count',sum(r.Duration)/60 as '使用分钟数',count(distinct c.MiaoguoCardID) from MiaoguoWXUsers u inner join WechatServiceWXUsers wu on u.UnionID=wu.UnionID inner join MiaoguoCard c on u.UserID=c.UserID inner join MiaoguoLearningRecord r on c.MiaoguoCardID = r.CardID where r.CreateTime>='2020-01-01' and r.CreateTime<'2021-01-01' and u.IsPay=1 and u.GroupID=1 group by u.UserID having Count>=100 order by Count desc;";
  2967. if (param.TypeDetail == 2) {
  2968. sql = "select wu.UserID,u.NickName,count(distinct DATE_FORMAT(r.CreateTime,'%Y%m%d')) as 'Count',sum(r.Duration)/60 as '使用分钟数',count(distinct c.MiaoguoCardID) from MiaoguoWXUsers u inner join WechatServiceWXUsers wu on u.UnionID=wu.UnionID inner join MiaoguoCard c on u.UserID=c.UserID inner join MiaoguoLearningRecord r on c.MiaoguoCardID = r.CardID where r.CreateTime>='2020-01-01' and r.CreateTime<'2021-01-01' and u.IsPay=1 and u.GroupID=2 group by u.UserID having Count>=50 order by Count desc;";
  2969. } else if (param.TypeDetail == 3) {
  2970. sql = "select wu.UserID,u.NickName,count(mt.ID) as 'Count' from MiaoguoTask mt inner join MiaoguoWXUsers u on mt.UserID=u.UserID inner join WechatServiceWXUsers wu on u.UnionID=wu.UnionID where mt.IsShare=1 and u.IsPay=1 and u.GroupID=1 and mt.CreateTime>='2020-12-07 00:00:00' and mt.CreateTime<'2021-01-06 00:00:00' group by mt.UserID order by count(mt.ID) desc;";
  2971. } else if (param.TypeDetail == 4) {
  2972. sql = "select wu.UserID,u.NickName,count(mt.ID) as 'Count' from MiaoguoTask mt inner join MiaoguoWXUsers u on mt.UserID=u.UserID inner join WechatServiceWXUsers wu on u.UnionID=wu.UnionID where mt.IsShare=1 and u.IsPay=1 and u.GroupID=2 and mt.CreateTime>='2020-12-07 00:00:00' and mt.CreateTime<'2021-01-06 00:00:00' group by mt.UserID order by count(mt.ID) desc;";
  2973. }
  2974. const list = await miaoguo.RunSql([], sql);
  2975. /*
  2976. * 旧系统实际发送语句处于注释状态。以下代码已经转换为 Node.js 24
  2977. * 写法;需要恢复该历史奖励时,可解除注释后先用 UserID=1 验证。
  2978. for (const item of list) {
  2979. let messageParam;
  2980. if (param.TypeDetail == 1) {
  2981. messageParam = { Title1: '2020年度学习勤勉奖', Title2: '奖励有效期[CouponNumber]天', Title3: '您练习我支持,奖励在2020年持续练习的孩子和家长,祝学业进步!', LimitTime: '2021-07-01', CouponNumber: '30' };
  2982. } else if (param.TypeDetail == 2) {
  2983. messageParam = { Title1: '2020年度学习勤勉奖', Title2: '奖励有效期[CouponNumber]天', Title3: '您练习我支持,奖励在2020年持续练习的孩子和家长,祝学业进步!', LimitTime: '2021-07-01', CouponNumber: '20' };
  2984. } else if (param.TypeDetail == 3 || param.TypeDetail == 4) {
  2985. messageParam = { Title1: '2020年第一学期期末备考30天冲刺奖', Title2: '奖励有效期[CouponNumber]天', Title3: '您练习我支持,奖励在2020年12月07日至2021年01月05日期间勤勉接力的孩子和家长,祝学业进步!', LimitTime: '2021-07-01', CouponNumber: String(item.Count) };
  2986. }
  2987. if (messageParam) {
  2988. await sendTemplateMessage(item.UserID, 'CouponSendRemind', JSON.stringify(messageParam));
  2989. await sleep(1000);
  2990. }
  2991. }
  2992. */
  2993. } else if (param.Type == 5) {
  2994. const sql = 'select u.NickName,w.UserID from MiaoguoWXUsers u inner join MiaoguoWXUsers u2 on u.Introducer=u2.UserID inner join WechatServiceWXUsers w on u2.UnionID=w.UnionID where u.IsPay=1 and u.ClassID=4;';
  2995. const list = await miaoguo.RunSql([], sql);
  2996. for (const item of list) {
  2997. if (item.UserID == 1) {
  2998. await sendTemplateMessage(item.UserID, 'IntroducerRemind', JSON.stringify({ NickName: item.NickName }));
  2999. }
  3000. await sleep(500);
  3001. }
  3002. } else if (param.Type == 6) {
  3003. const list = await miaoguo.RunSql([], 'select * from WechatServiceWXUsers where Subscribe=1 and IsProbation=0;');
  3004. for (const item of list) {
  3005. await sendTemplateMessage(item.UserID, 'SubscribeNoPayRemind');
  3006. await sleep(500);
  3007. }
  3008. } else if (param.Type == 7) {
  3009. const sql = "SELECT mt.UserID,u.NickName,u.AvatarUrl,sum(mt.IsShare) as '天数' FROM kylx365_db.MiaoguoTask mt " +
  3010. "inner join MiaoguoWXUsers u on mt.UserID=u.UserID " +
  3011. "where mt.CreateTime>='2019-09-01' and mt.CreateTime<'2019-10-01' and mt.IsShare=1 and u.UserID>7 " +
  3012. "group by mt.UserID having 天数>=1 order by 天数 desc;";
  3013. const list = await miaoguo.RunSql([], sql);
  3014. const limitTime = moment().add(6, 'months').format('YYYY-MM-DD');
  3015. for (const item of list) {
  3016. result.push("insert into MiaoguoCoupon (CouponType,UserID,LimitTime,IsUse,CouponNumber) values (137," + item.UserID + ",'" + limitTime + "',0," + item.天数 + ");");
  3017. }
  3018. } else if (param.Type == 8) {
  3019. /*
  3020. * 旧系统曾按 2024 年任务记录自动筛选名单,后来改成下面的人工确认名单。
  3021. * 保留已转换的旧查询,未来需要恢复自动筛选时可直接替换当前 sql。
  3022. const taskStatisticsSql = "SELECT mt.UserID,w.UserID as 'WUserID',u.NickName as '小程序昵称',w.NickName as '服务号昵称',u.NickNameRemark as '昵称备注',sum(mt.IsShare) as '群接力天数' " +
  3023. "FROM kylx365_db.MiaoguoTask mt inner join MiaoguoWXUsers u on mt.UserID=u.UserID " +
  3024. "inner join WechatServiceWXUsers w on u.UnionID=w.UnionID " +
  3025. "where mt.CreateTime>='2024-01-01' and mt.CreateTime<='2025-01-01' " +
  3026. "and mt.IsShare=1 and u.UserID>7 and u.ProductServiceTime>='2025-01-06' " +
  3027. "group by mt.UserID having 群接力天数>=20 order by 群接力天数 desc;";
  3028. */
  3029. const sql = "SELECT u.UserID,w.UserID as 'WUserID',u.NickName as '小程序昵称',w.NickName as '服务号昵称',u.NickNameRemark as '昵称备注' " +
  3030. "FROM MiaoguoWXUsers u inner join WechatServiceWXUsers w on u.UnionID=w.UnionID " +
  3031. "where u.UserID in " +
  3032. "(1,699,321,17161,15362,30894,36549,24084,16297,26887,21006,5806,199,31917,39733,34777,6168,29864,27170,27101,13239,33957,39392,40981,39421,30393,23093,22192,12683,24149,41274,12164,41795,41573,1760,15387,25473,33746,3232,31117,15456,23731,3438,41817,17110) " +
  3033. 'order by u.UserID;';
  3034. const list = await miaoguo.RunSql([], sql);
  3035. for (const item of list) {
  3036. const day = 20;
  3037. const messageParam = {
  3038. Title1: '2025年度有奖勤勉接力',
  3039. Title2: '赠送[CouponNumber]天',
  3040. Title3: '一年来辛苦您了,恭喜您和孩子在这不平凡的一年取得不平凡的成就。希望勤勉接力活动能为帮助孩子建立练习习惯起到积极作用,感谢您一直以来的支持。祝全家健康,学业进步,事业昌盛,新年快乐!',
  3041. LimitTime: '2026-07-11',
  3042. CouponNumber: String(day),
  3043. };
  3044. await sendTemplateMessage(item.WUserID, 'CouponSendRemind', JSON.stringify(messageParam), true);
  3045. result.push("insert into MiaoguoCoupon (CouponType,UserID,LimitTime,IsUse,CouponNumber) values (423," + item.UserID + ",'2026-07-11',0," + day + ");");
  3046. await sleep(500);
  3047. }
  3048. }
  3049. ctx.body = result.join('\n');
  3050. }
  3051. export async function BuildBookTotal(ctx) {
  3052. const confirmRun = ctx.query.ConfirmRun == 1;
  3053. const books = await miaoguo.RunSql(
  3054. [],
  3055. "SELECT * FROM HanziBook where Flag=0 and Name2 in ('上学期','下学期') order by Sort;",
  3056. );
  3057. const result = [];
  3058. for (const book of books) {
  3059. let wordNum = 0;
  3060. if (book.Name1 == "语文写字" || book.Name1 == "语文识字" || book.Name1 == "语文词语") {
  3061. const units = await hanzi.GetHanziUnits({ BookID: book.ID });
  3062. for (const unit of units) {
  3063. if (book.Name1 == "语文词语") {
  3064. const arr = String(unit.Example || "").split(",").filter(Boolean);
  3065. wordNum += arr.length;
  3066. } else {
  3067. wordNum += String(unit.Example || "").length;
  3068. }
  3069. }
  3070. } else if (book.Name1 == "语文古诗文" || book.Name1 == "语文日积月累") {
  3071. const poems = await hanzi.GetAncientPoetryList({ BookID: book.ID });
  3072. wordNum = poems.length;
  3073. } else {
  3074. continue;
  3075. }
  3076. result.push({ ID: book.ID, Name: book.Name, WordNum: wordNum });
  3077. if (confirmRun) {
  3078. await miaoguo.RunSql([wordNum, book.ID], "update HanziBook set WordNum=? where ID=?;");
  3079. }
  3080. }
  3081. ctx.body = { errcode: 10000, result, executed: confirmRun };
  3082. }
  3083. export async function UpdateUserConfigBasicCardLimit(ctx) {
  3084. const param = {
  3085. UserID: ctx.query.UserID || 0,
  3086. BasicCardLimit: ctx.query.BasicCardLimit || 0,
  3087. CurrentUserID: ctx.query.CurrentUserID || 0,
  3088. };
  3089. const log = {
  3090. UserID: ctx.query.UserID,
  3091. LogID: 314,
  3092. Content: JSON.stringify(param),
  3093. };
  3094. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  3095. delete param.CurrentUserID;
  3096. await miaoguo.UpdateUserConfig(param);
  3097. ctx.body = { errcode: 10000 };
  3098. }
  3099. export async function UploadUserConfig(ctx) {
  3100. const param = ctx.request.body || {};
  3101. param.UserID = Number(ctx.query.UserID || 0);
  3102. const dataType = ctx.query.Type || "getData";
  3103. param.CardType = Number(param.CardType) || 0;
  3104. param.CardMaxNumberNew = param.CardMaxNumberNew;
  3105. param.CardMaxNumberHistory = param.CardMaxNumberHistory || 100;
  3106. param.CardMaxNumberNewUrgent = param.CardMaxNumberNewUrgent;
  3107. param.CardMaxNumberHistoryUrgent = param.CardMaxNumberHistoryUrgent || 100;
  3108. param.SortTypeIndex = param.SortTypeIndex || 0;
  3109. param.MemoryLevel = param.MemoryLevel || 0;
  3110. param.ClickType = param.ClickType || 0;
  3111. param.FolderOrder = param.FolderOrder || "UpdateTime desc";
  3112. param.IsFolderPractice = param.IsFolderPractice || 0;
  3113. param.IsAutoSound = param.IsAutoSound;
  3114. const currentUserID = param.CurrentUserID;
  3115. delete param.CurrentUserID;
  3116. if (param.SecondConfigArray) {
  3117. if (stringUtils.IsArray(param.SecondConfigArray))
  3118. param.SecondConfigArray = param.SecondConfigArray.join(",");
  3119. } else {
  3120. param.SecondConfigArray = "0,0,0";
  3121. }
  3122. if (param.ColorIndexArr) {
  3123. if (stringUtils.IsArray(param.ColorIndexArr))
  3124. param.ColorIndexArr = param.ColorIndexArr.join(",");
  3125. } else {
  3126. param.ColorIndexArr = "1,2,3,4";
  3127. }
  3128. if (!param.IsAutoSound && param.IsAutoSound !== "0" && param.IsAutoSound !== 0)
  3129. param.IsAutoSound = 1;
  3130. if (!param.CardMaxNumberNew && param.CardMaxNumberNew != 0)
  3131. param.CardMaxNumberNew = 10;
  3132. if (!param.CardMaxNumberNewUrgent && param.CardMaxNumberNewUrgent != 0)
  3133. param.CardMaxNumberNewUrgent = 10;
  3134. if (dataType == "getData") {
  3135. const userList = await miaoguo.GetUserConfigData(param);
  3136. if (userList[0] && userList[0].SymbolList)
  3137. userList[0].SymbolList = JSON.parse(userList[0].SymbolList);
  3138. if (userList.length > 0)
  3139. ctx.body = { errcode: 10000, result: userList[0] };
  3140. else
  3141. ctx.body = { errcode: 10000, result: param };
  3142. return;
  3143. }
  3144. const dbParam = { ...param };
  3145. if (stringUtils.IsArray(dbParam.SymbolList))
  3146. dbParam.SymbolList = JSON.stringify(dbParam.SymbolList);
  3147. const userList = await miaoguo.GetUserConfigData(param);
  3148. if (userList.length > 0)
  3149. await miaoguo.UpdateUserConfig(dbParam);
  3150. else
  3151. await miaoguo.AddUserConfig(dbParam);
  3152. const logParam = { ...param, CurrentUserID: currentUserID };
  3153. const log = {
  3154. UserID: ctx.query.UserID,
  3155. LogID: 314,
  3156. Content: JSON.stringify(logParam),
  3157. };
  3158. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  3159. ctx.body = { errcode: 10000, result: logParam };
  3160. }
  3161. export async function DeleteAllMiaoguoCard(ctx) {
  3162. const param = {
  3163. UserID: ctx.query.UserID,
  3164. Flag: -1,
  3165. IsAll: 1,
  3166. };
  3167. param.DeleteTime = moment().format("YYYY-MM-DD HH:mm:ss");
  3168. param.UpdateTime = param.DeleteTime;
  3169. await miaoguo.UpdateCard(param);
  3170. const log = {
  3171. UserID: ctx.query.UserID,
  3172. LogID: 454,
  3173. Content: JSON.stringify(param),
  3174. };
  3175. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  3176. ctx.body = { errcode: 10000 };
  3177. }
  3178. export async function BuildInitData(ctx) {
  3179. const param = {
  3180. UserID: ctx.query.UserID || "0",
  3181. };
  3182. if (Number(param.UserID) > 0) {
  3183. const count = await miaoguo.GetCardCount(param);
  3184. if (count[0] && count[0].count == 0) {
  3185. const cardIDs = [];
  3186. for (let i = 0; i < 10; i++) {
  3187. const now = moment().format('YYYY-MM-DD HH:mm:ss');
  3188. const insertInfo = await miaoguo.AddCard({
  3189. UserID: param.UserID,
  3190. CreateTime: now,
  3191. LimitTime: now,
  3192. IsTodayStudy: 1,
  3193. });
  3194. cardIDs.push(insertInfo.insertId);
  3195. }
  3196. for (let i = 0; i < 10; i++) {
  3197. const listContent = await miaoguo.GetCardContentList(i + 1);
  3198. for (const contentType of [0, 1, 2, 3]) {
  3199. await miaoguo.AddCardContent({
  3200. MiaoguoCardID: cardIDs[i],
  3201. ContentType: contentType,
  3202. Content: listContent[contentType] ? listContent[contentType].Content : "",
  3203. });
  3204. }
  3205. }
  3206. }
  3207. }
  3208. ctx.body = { errcode: 10000, result: 1 };
  3209. }
  3210. export async function updateMiaoguoTaskData(ctx) {
  3211. const param = {
  3212. UserID: ctx.query.UserID || 0,
  3213. };
  3214. let startDate = moment().add(-30, "days").format("YYYY-MM-DD");
  3215. let userFilter = "";
  3216. if (Number(param.UserID) > 0) {
  3217. startDate = moment().add(-1200, "days").format("YYYY-MM-DD");
  3218. userFilter = " and mc.UserID=" + Number(param.UserID);
  3219. }
  3220. const sqlUsers = "select distinct mc.UserID from MiaoguoLearningRecord mlr inner join MiaoguoCard mc on mc.MiaoguoCardID=mlr.CardID where mlr.Flag=0 " + userFilter + " and mlr.CreateTime>=?;";
  3221. const list = await miaoguo.RunSql([startDate], sqlUsers);
  3222. const userIDs = [];
  3223. for (const item of list) {
  3224. const sqlDaily = "select DATE_FORMAT(r.CreateTime,'%Y-%m-%d') as CreateTime,count(distinct r.CardID) as Count,sum(r.Duration) as Sum " +
  3225. "from MiaoguoWXUsers u " +
  3226. "inner join MiaoguoCard c on u.UserID=c.UserID " +
  3227. "inner join MiaoguoLearningRecord r on c.MiaoguoCardID = r.CardID " +
  3228. "where r.Flag=0 and u.UserID=? and r.CreateTime>=? group by CreateTime order by CreateTime;";
  3229. const dailyList = await miaoguo.RunSql([item.UserID, startDate], sqlDaily);
  3230. for (const dayInfo of dailyList) {
  3231. const createTime = dayInfo.CreateTime + " 00:00:00";
  3232. const existing = await miaoguo.RunSql([item.UserID, createTime], "select * from MiaoguoTask where UserID=? and CreateTime=?;");
  3233. if (existing && existing.length > 0) {
  3234. await miaoguo.RunSql(
  3235. [dayInfo.Count, dayInfo.Sum, item.UserID, createTime],
  3236. "update MiaoguoTask set CardTask=?,Duration=? where UserID=? and CreateTime=?;",
  3237. );
  3238. } else {
  3239. await miaoguo.RunSql(
  3240. [item.UserID, createTime, dayInfo.Count, dayInfo.Sum],
  3241. "insert into MiaoguoTask (UserID,CreateTime,CardTask,Duration,IsShare,Flag) values (?,?,?,?,0,0);",
  3242. );
  3243. }
  3244. }
  3245. userIDs.push(item.UserID);
  3246. }
  3247. if (userIDs.length > 0) {
  3248. const ids = userIDs.map(id => Number(id)).filter(id => Number.isInteger(id) && id > 0).join(',');
  3249. const resetSql = Number(param.UserID) > 0
  3250. ? "update MiaoguoTask set MilestoneID=0,MilestoneDayNumber=0,HardworkingNumber=0 where UserID=?;"
  3251. : "update MiaoguoTask set MilestoneID=0,MilestoneDayNumber=0,HardworkingNumber=0 where UserID in (" + ids + ");";
  3252. await miaoguo.RunSql(Number(param.UserID) > 0 ? [Number(param.UserID)] : [], resetSql);
  3253. const taskUsers = await miaoguo.RunSql(
  3254. Number(param.UserID) > 0 ? [Number(param.UserID)] : [],
  3255. Number(param.UserID) > 0
  3256. ? "SELECT distinct UserID FROM MiaoguoTask where UserID=? order by UserID;"
  3257. : "SELECT distinct UserID FROM MiaoguoTask where UserID in (" + ids + ") order by UserID;",
  3258. );
  3259. const milestone = constantClass.GetMilestone();
  3260. for (const user of taskUsers) {
  3261. const tasks = await miaoguo.RunSql([user.UserID], "SELECT * FROM MiaoguoTask where CardTask>0 and UserID=? order by CreateTime;");
  3262. if (!tasks || tasks.length == 0) continue;
  3263. let milestoneIDs = [];
  3264. for (let i = 0; i < 40; i++) milestoneIDs.push(i + 1);
  3265. milestoneIDs = stringUtils.RandomArray(milestoneIDs);
  3266. let milestoneIndex = 0;
  3267. let hardworkingIndex = 0;
  3268. for (let i = 0; i < tasks.length; i++) {
  3269. const update = {
  3270. ID: tasks[i].ID,
  3271. DayNumber: i + 1,
  3272. };
  3273. const milestoneItem = milestone.find(item => item.DayNumber == i + 1);
  3274. if (milestoneItem) {
  3275. update.MilestoneDayNumber = milestoneItem.DayNumber;
  3276. update.MilestoneID = milestoneIDs[milestoneIndex++];
  3277. }
  3278. if (tasks[i].IsShare > 0 || update.MilestoneDayNumber > 0 || tasks[i].AphorismID > 0)
  3279. update.HardworkingNumber = ++hardworkingIndex;
  3280. await miaoguo.UpdateMiaoguoTask(update);
  3281. }
  3282. }
  3283. }
  3284. ctx.body = { errcode: 10000, result: { UserCount: userIDs.length } };
  3285. }
  3286. export async function updateMiaoguoFamily(ctx) {
  3287. const param = {
  3288. UserID: ctx.query.UserID || 0,
  3289. UpdateType: ctx.query.UpdateType || "add",
  3290. NewUserID: ctx.query.NewUserID || 0,
  3291. Time: ctx.query.Time,
  3292. };
  3293. let result = 0;
  3294. const yestoday = moment().add(-10, "minutes").format("YYYY-MM-DD HH:mm:ss");
  3295. if (yestoday > param.Time) {
  3296. result = -1;
  3297. } else {
  3298. const list = await miaoguo.GetMiaoguoFamilyByUserID(param);
  3299. if (list && list.length > 0) {
  3300. if (param.UpdateType == "delete") {
  3301. const obj = {
  3302. ID: list[0].ID,
  3303. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  3304. Flag: -1,
  3305. };
  3306. await miaoguo.UpdateMiaoguoFamily(obj);
  3307. miaoguo.AddMiaoguoLog({ UserID: ctx.query.UserID, LogID: 372, Content: JSON.stringify(param) }).catch(err => console.error('AddMiaoguoLog failed:', err));
  3308. } else if (param.UpdateType == "add") {
  3309. if (list[0].UserID1 == param.NewUserID)
  3310. result = -2;
  3311. if (list[0].UserID2 == param.NewUserID)
  3312. result = -2;
  3313. if (list[0].UserID3 == param.NewUserID)
  3314. result = -2;
  3315. if (list[0].UserID1 > 0 && list[0].UserID2 > 0 && list[0].UserID3 > 0)
  3316. result = -3;
  3317. if (result == 0) {
  3318. const obj = {
  3319. ID: list[0].ID,
  3320. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  3321. };
  3322. if (!list[0].UserID1 || list[0].UserID1 == "")
  3323. obj.UserID1 = param.NewUserID;
  3324. else if (!list[0].UserID2 || list[0].UserID2 == "")
  3325. obj.UserID2 = param.NewUserID;
  3326. else if (!list[0].UserID3 || list[0].UserID3 == "")
  3327. obj.UserID3 = param.NewUserID;
  3328. await miaoguo.UpdateMiaoguoFamily(obj);
  3329. miaoguo.AddMiaoguoLog({ UserID: ctx.query.UserID, LogID: 373, Content: JSON.stringify(param) }).catch(err => console.error('AddMiaoguoLog failed:', err));
  3330. }
  3331. }
  3332. } else if (param.UpdateType == "add") {
  3333. const obj = {
  3334. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  3335. UserID1: param.UserID,
  3336. UserID2: param.NewUserID,
  3337. Flag: 0,
  3338. };
  3339. await miaoguo.AddMiaoguoFamily(obj);
  3340. miaoguo.AddMiaoguoLog({ UserID: ctx.query.UserID, LogID: 373, Content: JSON.stringify(param) }).catch(err => console.error('AddMiaoguoLog failed:', err));
  3341. }
  3342. }
  3343. ctx.body = { errcode: 10000, result };
  3344. }
  3345. export async function GetMiaoguoCheckList(ctx) {
  3346. const param = {
  3347. UserID: ctx.query.UserID || 0,
  3348. FieldStr: 'UserID,IsMember,NickName,AvatarUrl',
  3349. };
  3350. const result = {};
  3351. result.List = [
  3352. {},
  3353. { Title: '加入秒过学习班群(微信群)', Max: 1 },
  3354. { Title: '收集孩子的错题', Max: 1 },
  3355. { Title: '第一课', Remark: "概念课:学习法与记忆规律(约30分钟)", Max: 50, Finished: 0 },
  3356. { Title: '第二课', Remark: "操作课:实战制作合格的题卡(约20分钟)", Max: 50, Finished: 0 },
  3357. { Title: '第三课', Remark: "操作课:陪伴孩子完成学习任务(约20分钟)", Max: 50, Finished: 0 },
  3358. { Title: '第四课', Remark: "精要解析(约10分钟)", Max: 50, Finished: 0 },
  3359. { Title: '添加30张题卡', Max: 30 },
  3360. { Title: '带领孩子完成20分钟学习任务', Max: 1200 },
  3361. { Title: '分享题卡到班群3次', Max: 3 },
  3362. { Title: '勤勉接力到班群3天', Max: 3 },
  3363. ];
  3364. const lessonCount = [0, 28, 7, 6, 14];
  3365. const userInfo = await miaoguo.GetUsersInfoByUserID(param);
  3366. if (userInfo && userInfo.length > 0) {
  3367. result.UserID = userInfo[0].UserID;
  3368. result.NickName = userInfo[0].NickName;
  3369. result.AvatarUrl = userInfo[0].AvatarUrl;
  3370. if (userInfo[0].IsMember == 1) {
  3371. result.List[1].Finished = 1;
  3372. result.List[2].Finished = 1;
  3373. }
  3374. const lesson = await miaoguo.GetUserMiaoguoLesson(param);
  3375. if (lesson && lesson.length > 0) {
  3376. const lessonCount2 = ["", "", "", "", ""];
  3377. for (const item of lesson)
  3378. lessonCount2[item.LessonID] += "," + item.Content;
  3379. for (let i = 1; i < lessonCount2.length; i++) {
  3380. if (lessonCount2[i].length > 0) {
  3381. lessonCount2[i] = _.uniq(lessonCount2[i].substr(1).split(","));
  3382. result.List[i + 2].Finished = Math.round(100 * lessonCount2[i].length / lessonCount[i]);
  3383. } else {
  3384. result.List[i + 2].Finished = 0;
  3385. }
  3386. }
  3387. }
  3388. param.CheckList = 1;
  3389. const cardNumber = await miaoguo.GetCardCount(param);
  3390. result.List[7].Finished = 0;
  3391. if (cardNumber && cardNumber.length > 0)
  3392. result.List[7].Finished = cardNumber[0].count;
  3393. delete param.CheckList;
  3394. param.CreateTime = "2019-03-01";
  3395. const taskData = await miaoguo.GetTaskData(param);
  3396. result.List[8].Finished = 0;
  3397. if (taskData && taskData.length > 0 && taskData[0].Duration)
  3398. result.List[8].Finished = taskData[0].Duration;
  3399. result.List[8].FinishedStr = stringUtils.getMinuteSecond(result.List[8].Finished);
  3400. const cardShare = await miaoguo.GetUserMiaoguoCardShare(param);
  3401. result.List[9].Finished = 0;
  3402. if (cardShare && cardShare.length > 0)
  3403. result.List[9].Finished = cardShare.length;
  3404. const taskShare = await miaoguo.GetUserMiaoguoTaskShare(param);
  3405. result.List[10].Finished = 0;
  3406. if (taskShare && taskShare.length > 0)
  3407. result.List[10].Finished = taskShare.length;
  3408. }
  3409. let finishCount = 0;
  3410. for (let i = 1; i < result.List.length; i++) {
  3411. if (result.List[i].Finished >= result.List[i].Max)
  3412. finishCount++;
  3413. }
  3414. result.FinishedCount = Math.round(100 * finishCount / (result.List.length - 1));
  3415. if (result.FinishedCount == 100)
  3416. await miaoguo.UpdateUsersByUserID({ UserID: param.UserID, IsFinishTask: 1 });
  3417. ctx.body = { errcode: 10000, result };
  3418. }
  3419. export async function GetAnnualReport(ctx) {
  3420. const param = {
  3421. UserID: ctx.query.UserID || 0,
  3422. TimeStart: ctx.query.Time || "2024-01-01 00:00:00",
  3423. TimeEnd: ctx.query.Time || "2024-12-31 23:59:59",
  3424. };
  3425. const result = {};
  3426. let sql = `select u.UserID,count(distinct DATE_FORMAT(r.CreateTime,'%Y%m%d')) as 'DayNumber'
  3427. from MiaoguoWXUsers u inner join MiaoguoCard c on u.UserID=c.UserID inner join MiaoguoLearningRecord r on c.MiaoguoCardID = r.CardID
  3428. where r.Flag=0 and r.CreateTime>='${param.TimeStart}' and r.CreateTime<='${param.TimeEnd}' and IsPay=1
  3429. group by u.UserID order by DayNumber desc;`;
  3430. let result1 = await miaoguo.RunSql({}, sql);
  3431. if (result1 && result1.length > 0) {
  3432. let index = 0;
  3433. for (let i = 0; i < result1.length; i++) {
  3434. if (result1[i].UserID == param.UserID) {
  3435. index = i;
  3436. break;
  3437. }
  3438. }
  3439. result.Hard = Math.round(10000 * (result1.length - index) / result1.length) / 100 + "%";
  3440. }
  3441. sql = `SELECT m.UserID,m.CreateTime,Count(distinct DATE_FORMAT(mlr.CreateTime,'%Y-%m-%d')) as 'DayNum',Count(mlr.ID) as 'RememberNum',Sum(mlr.Duration) as 'PracticeTime'
  3442. FROM MiaoguoWXUsers m left join MiaoguoCard mc on m.UserID=mc.UserID left join MiaoguoLearningRecord mlr on mc.MiaoguoCardID=mlr.CardID
  3443. where mlr.Flag=0 and m.UserID=${param.UserID} and mlr.CreateTime>='${param.TimeStart}' and mlr.CreateTime<='${param.TimeEnd}';`;
  3444. result1 = await miaoguo.RunSql({}, sql);
  3445. if (result1 && result1.length > 0) {
  3446. result.StartDate = moment(result1[0].CreateTime).format("YYYY年MM月DD日");
  3447. if (result.StartDate < moment(param.TimeStart).format("YYYY年MM月DD日"))
  3448. result.StartDate = moment(param.TimeStart).format("YYYY年MM月DD日");
  3449. result.EndDate = moment(param.TimeEnd).format("YYYY年MM月DD日");
  3450. result.DayNum = result1[0].DayNum;
  3451. result.RememberNum = result1[0].RememberNum;
  3452. result.PracticeTime = stringUtils.getMinuteSecond(result1[0].PracticeTime, true);
  3453. }
  3454. const shareInfo = await miaoguo.GetUserMiaoguoTaskShare(param);
  3455. result.ShareNum = 0;
  3456. if (shareInfo && shareInfo.length > 0)
  3457. result.ShareNum = shareInfo.length;
  3458. sql = `SELECT DATE_FORMAT(mlr.CreateTime,'%m月') as MonthName,Sum(mlr.Duration)/60 as 'PracticeTime'
  3459. FROM MiaoguoCard mc left join MiaoguoLearningRecord mlr on mc.MiaoguoCardID=mlr.CardID
  3460. where mlr.Flag=0 and mc.UserID=${param.UserID} and DATE_FORMAT(mlr.CreateTime,'%Y-%m') is not null
  3461. and mlr.CreateTime>='${param.TimeStart}' and mlr.CreateTime<='${param.TimeEnd}' group by DATE_FORMAT(mlr.CreateTime,'%Y-%m');`;
  3462. result1 = await miaoguo.RunSql({}, sql);
  3463. if (result1 && result1.length > 0) {
  3464. for (const item of result1)
  3465. item.PracticeTime = Math.round(item.PracticeTime);
  3466. result.PracticeMonth = result1;
  3467. }
  3468. sql = `SELECT DATE_FORMAT(mc.CreateTime,'%Y%m') as MonthName,Count(mc.MiaoguoCardID) as 'CardNum'
  3469. FROM MiaoguoCard mc where mc.UserID=${param.UserID} and mc.CreateTime>='${param.TimeStart}'
  3470. and mc.CreateTime<='${param.TimeEnd}' and mc.Flag=0 group by DATE_FORMAT(mc.CreateTime,'%Y-%m');`;
  3471. result1 = await miaoguo.RunSql({}, sql);
  3472. if (result1 && result1.length > 0) {
  3473. result.CardNull = "";
  3474. const monthStart = Number(result1[0].MonthName);
  3475. const monthEnd = Number(moment(param.TimeEnd).format("YYYYMM"));
  3476. for (let j = monthStart; j <= monthEnd; j++) {
  3477. let hasMonth = false;
  3478. for (const item of result1) {
  3479. if (j == Number(item.MonthName)) {
  3480. hasMonth = true;
  3481. break;
  3482. }
  3483. }
  3484. if (!hasMonth)
  3485. result.CardNull += Number(j.toString().substr(4)) + " ";
  3486. }
  3487. result.CardNull = stringUtils.Trim(result.CardNull);
  3488. if (!result.CardNull)
  3489. result.CardNull = "每月都有";
  3490. }
  3491. sql = `Select Count(*) as CardNum from MiaoguoCard where UserID=${param.UserID} and Flag=0;`;
  3492. result1 = await miaoguo.RunSql({}, sql);
  3493. if (result1 && result1.length > 0)
  3494. result.CardNum = result1[0].CardNum;
  3495. sql = `Select Count(*) as NewCardNum from MiaoguoCard where UserID=${param.UserID} and Flag=0 and CreateTime>='${param.TimeStart}' and CreateTime<='${param.TimeEnd}';`;
  3496. result1 = await miaoguo.RunSql({}, sql);
  3497. if (result1 && result1.length > 0)
  3498. result.NewCardNum = result1[0].NewCardNum;
  3499. sql = `SELECT BtnNumber,Count(mlr.ID) as 'MemoryEvaluation'
  3500. FROM MiaoguoCard mc left join MiaoguoLearningRecord mlr on mc.MiaoguoCardID=mlr.CardID
  3501. where mlr.Flag=0 and mc.UserID=${param.UserID} and mlr.BtnNumber>=0 and mlr.BtnNumber<>2
  3502. and mlr.CreateTime>='${param.TimeStart}' and mlr.CreateTime<='${param.TimeEnd}' group by BtnNumber;`;
  3503. result1 = await miaoguo.RunSql({}, sql);
  3504. if (result1 && result1.length > 0) {
  3505. let memoryEvaluationTotal = 0;
  3506. for (const item of result1)
  3507. memoryEvaluationTotal += item.MemoryEvaluation;
  3508. const obj = {};
  3509. for (const item of result1) {
  3510. const rate = Math.round(100 * item.MemoryEvaluation / memoryEvaluationTotal) + "%";
  3511. switch (item.BtnNumber) {
  3512. case 0:
  3513. obj.Shelve = rate;
  3514. break;
  3515. case 1:
  3516. obj.Red = rate;
  3517. break;
  3518. case 3:
  3519. obj.Yellow = rate;
  3520. break;
  3521. case 4:
  3522. obj.Green = rate;
  3523. break;
  3524. case 5:
  3525. obj.Blue = rate;
  3526. break;
  3527. }
  3528. }
  3529. if (!obj.Shelve)
  3530. obj.Shelve = "0%";
  3531. if (!obj.Red)
  3532. obj.Red = "0%";
  3533. if (!obj.Yellow)
  3534. obj.Yellow = "0%";
  3535. if (!obj.Green)
  3536. obj.Green = "0%";
  3537. if (!obj.Blue)
  3538. obj.Blue = "0%";
  3539. result.MemoryEvaluation = obj;
  3540. }
  3541. ctx.body = { errcode: 10000, result };
  3542. }
  3543. export async function GetAwardInfo(ctx) {
  3544. const param = {
  3545. UserID: ctx.query.UserID || 0,
  3546. Time: ctx.query.Time || "",
  3547. };
  3548. if (!param.Time)
  3549. param.Time = moment().add(-7, "days").format("YYYY-MM-DD HH:mm:ss");
  3550. const result = {};
  3551. const info = await miaoguo.GetAwardInfo(param);
  3552. const num1 = info[0].Count;
  3553. param.IsWebchat = true;
  3554. const info1 = await miaoguo.GetAwardInfo(param);
  3555. const num2 = info1[0].Count;
  3556. delete param.IsWebchat;
  3557. param.IsBill = true;
  3558. result.AwardDayNumber = 0;
  3559. const info2 = await miaoguo.GetAwardInfo(param);
  3560. if (info2 && info2.length > 0 && info2[0].BillTime) {
  3561. result.BillTime = moment(info2[0].BillTime).format("YYYY年MM月DD日");
  3562. result.AwardDayNumber = Number(info2[0].Count);
  3563. }
  3564. result.List = [
  3565. { Name: "好友报名", PersonNum: Number(num2), Content: "奖励5天/人", Total: Number(num2) * 5 + "天" },
  3566. { Name: "好友续费", PersonNum: Number(num1), Content: "奖励115天/人", Total: Number(num1) * 115 + "天" },
  3567. ];
  3568. result.Time = moment(param.Time).format("YYYY年MM月DD日");
  3569. result.Remark = "报名人数含续费人数(流程为先报名后续费),在续费奖励的120天里,包含了报名奖励的5天。为方便向您说明,表格以单项形式列支,所以续费奖励独立显示为115天。";
  3570. result.Total = Number(num2) * 5 + Number(num1) * 115;
  3571. ctx.body = { errcode: 10000, result };
  3572. }
  3573. export async function GetApprovalUserList(ctx) {
  3574. const param = {
  3575. ClassID: ctx.query.ClassID || 0,
  3576. };
  3577. const url = "GetApprovalUserList";
  3578. let result = globalCache.get(url);
  3579. if (result === 0) {
  3580. result = [];
  3581. let arr = [8, 9, 10, 11, 12, 13, 105, 141, 204, 206, 209, 216, 222, 229, 238, 248, 252, 402, 526, 568, 571, 787, 1355, 1356, 6137, 8055, 12363];
  3582. const list2 = await payInfo.GetMiaoguoPayInfoList();
  3583. for (const item of list2)
  3584. arr.push(item.UserID);
  3585. arr = _.concat(arr, 348, 364, 456, 597, 699, 735, 1025, 1018, 1009, 1021, 1017);
  3586. const list3 = await payInfo.GetMiaoguoPayInfoList({ webtype: "web" });
  3587. for (const item of list3)
  3588. arr.push(item.UserID);
  3589. arr.sort();
  3590. arr = _.union(arr);
  3591. let list = await miaoguo.GetApprovalUserList(param);
  3592. for (const item of list) {
  3593. for (const userID of arr) {
  3594. if (userID == item.UserID) {
  3595. item.CreateTime = moment(item.CreateTime).format('YY.MM.DD HH:mm');
  3596. item.LastLoginTime = moment(item.LastLoginTime).format('YY.MM.DD HH:mm');
  3597. if (item.IsProbation == 1) {
  3598. const time1 = moment().format("YYYYMMDDHHmm");
  3599. const time2 = moment(item.LimitTime).add(48, "hours").format("YYYYMMDDHHmm");
  3600. if (time1 > time2)
  3601. item.CssBg = "background-color: #D32F2F;";
  3602. if (item.ProbationPayTime)
  3603. item.ProbationPayTime = moment(item.ProbationPayTime).format('YY.MM.DD HH:mm');
  3604. else {
  3605. if (item.UserID > 199)
  3606. item.ProbationPayTime = "*";
  3607. else
  3608. item.ProbationPayTime = "#";
  3609. }
  3610. } else {
  3611. item.ProbationPayTime = "";
  3612. }
  3613. if (!item.Subscribe)
  3614. item.Subscribe = "";
  3615. else
  3616. item.Subscribe = moment(item.SubscribeTime).format('YY.MM.DD HH:mm');
  3617. if (item.IsMember == 1) {
  3618. item.ButtonOn = true;
  3619. item.CssBg = "background-color: #e2ffd3;";
  3620. } else {
  3621. item.ButtonOn = false;
  3622. item.CssBg = "background-color: #fff;";
  3623. }
  3624. if (!item.PayTime)
  3625. item.PayTime = "";
  3626. else
  3627. item.PayTime = moment(item.PayTime).format('YY.MM.DD HH:mm');
  3628. if (item.NickName != item.WSUNickName)
  3629. item.NickName += '[' + item.WSUNickName + ']';
  3630. if (item.NickNameRemark)
  3631. item.NickName += '「' + item.NickNameRemark + '」';
  3632. item.GroupID = stringUtils.getGroupName(item.GroupID);
  3633. if (item.Introducer) {
  3634. if (item.IntroducerClassID == 1)
  3635. item.IntroducerClassID = "1~5";
  3636. item.IntroducerGroupID = stringUtils.getGroupName(item.IntroducerGroupID);
  3637. item.Introducer = item.Introducer + "(" + item.IntroducerClassID + "班 " + item.IntroducerGroupID + ")";
  3638. }
  3639. result.push(item);
  3640. break;
  3641. }
  3642. }
  3643. }
  3644. param.type = "NoneMiaoguoData";
  3645. list = await miaoguo.GetApprovalUserList(param);
  3646. for (const item of list) {
  3647. const obj = {};
  3648. obj.UserID = "";
  3649. obj.WSUUserID = item.UserID;
  3650. obj.NickName = "[" + item.NickName + "]";
  3651. obj.WSUAvatarUrl = item.AvatarUrl;
  3652. obj.CreateTime = "";
  3653. obj.LastLoginTime = "";
  3654. if (!item.Subscribe)
  3655. obj.Subscribe = "";
  3656. else
  3657. obj.Subscribe = moment(item.SubscribeTime).format('YY.MM.DD HH:mm');
  3658. if (item.IsProbation == 1) {
  3659. if (item.ProbationPayTime)
  3660. obj.ProbationPayTime = moment(item.ProbationPayTime).format('YY.MM.DD HH:mm');
  3661. else
  3662. obj.ProbationPayTime = "";
  3663. } else {
  3664. obj.ProbationPayTime = "";
  3665. }
  3666. obj.ClassID = item.TempClassID;
  3667. obj.IsMember = item.TempIsMember;
  3668. obj.PayTime = "";
  3669. if (obj.IsMember == 1) {
  3670. obj.ButtonOn = true;
  3671. obj.CssBg = "background-color: #e2ffd3;";
  3672. } else {
  3673. obj.ButtonOn = false;
  3674. obj.CssBg = "background-color: #fff;";
  3675. }
  3676. if (item.IntroducerUserID)
  3677. obj.Introducer = item.IntroducerNickName + "(" + item.IntroducerUserID + ")";
  3678. delete obj.IntroducerUserID;
  3679. delete obj.IntroducerNickName;
  3680. result.unshift(obj);
  3681. }
  3682. globalCache.set(url, result, config.BufferMemoryTimeLower);
  3683. }
  3684. const arr = [];
  3685. for (const item of result) {
  3686. if (item.ClassID == param.ClassID || param.ClassID == "All") {
  3687. if (item.ClassID == 0 && item.IsMember == 1 && item.Subscribe == 0) {
  3688. // Keep old behavior: skip this row.
  3689. } else {
  3690. arr.push(item);
  3691. }
  3692. }
  3693. }
  3694. ctx.body = { errcode: 10000, result: arr };
  3695. }
  3696. export async function GetMiaoguoTestIndex(ctx) {
  3697. const param = {
  3698. UserID: ctx.query.UserID || 0,
  3699. };
  3700. const resultAll = {};
  3701. const userInfo = await miaoguo.GetUsersInfoByUserID(param);
  3702. if (userInfo && userInfo.length) {
  3703. const param2 = {
  3704. UserID: param.UserID,
  3705. Grade: userInfo[0].Grade,
  3706. UpperAndLowerSemesters: userInfo[0].UpperAndLowerSemesters,
  3707. };
  3708. const list = await miaoguo.GetMiaoguoTestIndex(param2);
  3709. const result = {
  3710. List1: [],
  3711. List2: [],
  3712. List3: [],
  3713. List4: [],
  3714. };
  3715. for (const item of list) {
  3716. if (item.Sort2 == 1) {
  3717. item.Name1 = item.Name1.replace("语文", "");
  3718. item.Name1 = item.Name1.replace("拼音基础发音", "拼音基础");
  3719. item.Name1 = item.Name1.replace("拼音发音组合", "拼音组合");
  3720. delete item.BookImageName;
  3721. }
  3722. item.KnowledgeImageName = "pic_" + item.KnowledgeImageName;
  3723. if (item.Sort2 == 1)
  3724. result.List1.push(item);
  3725. else if (item.Sort2 == 2)
  3726. result.List2.push(item);
  3727. else if (item.Sort2 == 3)
  3728. result.List3.push(item);
  3729. else if (item.Sort2 == 4)
  3730. result.List4.push(item);
  3731. }
  3732. if (userInfo[0].Grade >= 7) {
  3733. const temp = result.List2[1];
  3734. result.List2[1] = result.List2[0];
  3735. result.List2[0] = temp;
  3736. }
  3737. const arrTemp = [];
  3738. let arrTemp2 = [];
  3739. for (let k = 0; k < result.List4.length; k++) {
  3740. if (k > 0 && k % 3 == 0) {
  3741. arrTemp.push(arrTemp2);
  3742. arrTemp2 = [];
  3743. }
  3744. arrTemp2.push(result.List4[k]);
  3745. }
  3746. if (arrTemp2.length > 0)
  3747. arrTemp.push(arrTemp2);
  3748. result.List4 = arrTemp;
  3749. result.Grade = stringUtils.GetGrade2(param2.Grade);
  3750. result.UpperAndLowerSemesters = userInfo[0].UpperAndLowerSemesters;
  3751. resultAll.List1 = result;
  3752. const reportParam = {
  3753. UserID: param.UserID,
  3754. BookID: 0,
  3755. };
  3756. resultAll.List2 = await miaoguo.GetMiaoguoTestExamineReport2(reportParam);
  3757. for (const item of resultAll.List2) {
  3758. item.TestTime = moment(item.TestTime).format("YYYY年MM月DD日 HH:mm");
  3759. if (item.JSONData) {
  3760. const arr = JSON.parse(item.JSONData);
  3761. item.RightNum = arr[2].length;
  3762. item.WrongNum = arr[0].length + arr[1].length;
  3763. delete item.JSONData;
  3764. } else {
  3765. item.TestTime += " 已封存";
  3766. }
  3767. }
  3768. }
  3769. ctx.body = { errcode: 10000, result: resultAll };
  3770. }
  3771. export async function GetMiaoguoTestLibrary(ctx) {
  3772. const url = "GetMiaoguoTestLibrary";
  3773. let result = globalCache.get(url);
  3774. if (result === 0) {
  3775. result = await miaoguo.GetMiaoguoBook();
  3776. for (const item of result) {
  3777. let str = "cn";
  3778. if (item.Category == "English")
  3779. str = "en";
  3780. item.BookImageName = "picZs_" + str + "_" + item.BookImageName + "_cov";
  3781. }
  3782. globalCache.set(url, result, config.BufferMemoryTimeHigher);
  3783. }
  3784. ctx.body = { errcode: 10000, result };
  3785. }
  3786. export async function GetMiaoguoTestPackage(ctx) {
  3787. const param = {
  3788. UserID: ctx.query.UserID || 0,
  3789. Category: ctx.query.Category || "",
  3790. LibraryName1: ctx.query.LibraryName1 || "",
  3791. LibraryName2: ctx.query.LibraryName2 || "",
  3792. };
  3793. const url = "GetMiaoguoTestPackage?Category=" + param.Category + "&LibraryName1=" + param.LibraryName1 + "&LibraryName2=" + param.LibraryName2;
  3794. let result = globalCache.get(url);
  3795. if (result === 0) {
  3796. result = await miaoguo.GetMiaoguoPackage(param);
  3797. for (const item of result) {
  3798. item.TestFunction = JSON.parse(item.TestFunction);
  3799. let strCategory = "en";
  3800. if (item.Category == "语文")
  3801. strCategory = "cn";
  3802. item.KnowledgeImageName = "picZs_" + strCategory + "_" + item.BookImageName + "_" + item.KnowledgeImageName;
  3803. }
  3804. globalCache.set(url, result, config.BufferMemoryTimeHigh);
  3805. }
  3806. if (param.UserID) {
  3807. const list = await miaoguo.GetMiaoguoTestExamineReport(param);
  3808. for (const item of result) {
  3809. for (const report of list) {
  3810. if (item.BookName == report.BookName) {
  3811. for (const fn of item.TestFunction) {
  3812. if (fn.N == "read") {
  3813. if (report.ReadNum > item.UnitNum)
  3814. report.ReadNum = item.UnitNum;
  3815. fn.C = Math.round(100 * report.ReadNum / item.UnitNum);
  3816. } else if (fn.N == "write") {
  3817. if (report.WriteNum > item.UnitNum)
  3818. report.WriteNum = item.UnitNum;
  3819. fn.C = Math.round(100 * report.WriteNum / item.UnitNum);
  3820. }
  3821. }
  3822. }
  3823. }
  3824. }
  3825. }
  3826. ctx.body = { errcode: 10000, result };
  3827. }
  3828. export function buildCambridgeCategoryUnits(wordList, testFunction) {
  3829. const units = [];
  3830. for (let start = 0; start < wordList.length; start += 10) {
  3831. const words = wordList.slice(start, start + 10).map(item => item.Word);
  3832. const id = units.length + 1;
  3833. units.push({
  3834. ID: id,
  3835. UnitName: "Lesson " + id,
  3836. ListStr: words.join("、"),
  3837. Length: words.length,
  3838. TestFunction: testFunction,
  3839. });
  3840. }
  3841. return units;
  3842. }
  3843. export async function GetMiaoguoTestUnit(ctx) {
  3844. const param = {
  3845. UserID: ctx.query.UserID || 0,
  3846. BookID: ctx.query.BookID || "0",
  3847. };
  3848. const url = "GetMiaoguoTestUnit?BookID=" + param.BookID;
  3849. let result = globalCache.get(url);
  3850. if (result === 0) {
  3851. const info = await miaoguo.GetMiaoguoPackage(param);
  3852. info[0].TestFunction = JSON.parse(info[0].TestFunction);
  3853. let strCategory = "en";
  3854. if (info[0].Category == "语文")
  3855. strCategory = "cn";
  3856. info[0].KnowledgeImageName = "picZs_" + strCategory + "_" + info[0].BookImageName + "_" + info[0].KnowledgeImageName;
  3857. if (info[0].Category == "语文") {
  3858. if (info[0].BookIDOld == 43) {
  3859. result = constantClass.arrPinyinList;
  3860. for (const item of result) {
  3861. item.UnitName = item.LessonName;
  3862. if (item.List && item.List.length > 0) {
  3863. item.ListStr = item.List.join("、");
  3864. item.Length = item.List.length;
  3865. } else {
  3866. item.ListStr = "";
  3867. }
  3868. item.TestFunction = info[0].TestFunction;
  3869. delete item.List;
  3870. }
  3871. } else if (info[0].BookIDOld == 44) {
  3872. result = constantClass.getPinyinSoundList;
  3873. for (const item of result) {
  3874. item.UnitName = item.Name;
  3875. item.ListStr = item.ListStr;
  3876. item.TestFunction = info[0].TestFunction;
  3877. }
  3878. } else if (info[0].BookIDOld == 86) {
  3879. result = await hanzi.GetAncientChineseProseSentence();
  3880. for (const item of result) {
  3881. item.ID = item.UnitID;
  3882. item.UnitName = item.Unit;
  3883. item.ListStr = "";
  3884. item.Length = item.Count;
  3885. item.TestFunction = info[0].TestFunction;
  3886. delete item.UnitID;
  3887. delete item.Count;
  3888. delete item.Unit;
  3889. }
  3890. } else if (info[0].BookIDOld == 73) {
  3891. result = await hanzi.GetAncientChineseProse({ IsList: true });
  3892. for (const item of result) {
  3893. item.ID = item.UnitID;
  3894. item.UnitName = item.Word;
  3895. item.ListStr = "";
  3896. item.Length = item.Count;
  3897. item.TestFunction = info[0].TestFunction;
  3898. delete item.UnitID;
  3899. delete item.Count;
  3900. delete item.Word;
  3901. }
  3902. } else if (info[0].BookName == "古诗文" || info[0].BookName == "日积月累") {
  3903. result = await hanzi.GetAncientPoetryList({ BookID: info[0].BookIDOld });
  3904. const item = {
  3905. ID: info[0].BookIDOld,
  3906. UnitName: info[0].BookName,
  3907. List: [],
  3908. };
  3909. for (const poetry of result) {
  3910. item.List.push(poetry.Title);
  3911. item.TestFunction = info[0].TestFunction;
  3912. }
  3913. item.ListStr = item.List.join(",");
  3914. item.Length = item.List.length;
  3915. result = [item];
  3916. } else {
  3917. result = await hanzi.GetHanziUnits({ BookID: info[0].BookIDOld });
  3918. for (const item of result) {
  3919. if (item.Example && item.Example.indexOf(",") > 0)
  3920. item.List = item.Example.split(",");
  3921. else
  3922. item.List = item.Example.split("");
  3923. item.ListStr = item.List.join("、");
  3924. item.UnitName = item.Name;
  3925. item.Length = item.List.length;
  3926. delete item.List;
  3927. item.TestFunction = info[0].TestFunction;
  3928. }
  3929. }
  3930. } else {
  3931. let temp = null;
  3932. let item = { List: [] };
  3933. const arr = [];
  3934. if (info[0].BookIDOld == 100) {
  3935. item.ID = 1001;
  3936. item.UnitName = "英文字母";
  3937. item.ListStr = "A、B、C、D、E、F、G、H、I、J、K、L、M、N、O、P、Q、R、S、T、U、V、W、X、Y、Z";
  3938. item.List = item.ListStr.split("、");
  3939. item.Length = 26;
  3940. item.TestFunction = info[0].TestFunction;
  3941. arr.push(item);
  3942. } else if (info[0].BookIDOld == 101) {
  3943. result = [{ "ID": 1, "Name": "单元音", "Words": ["/i:/", "/ɪ/", "/æ/", "/e/", "/ɜ:/", "/ə/", "/a:/", "/ʌ/", "/ɔ:/", "/ɔ/", "/u:/", "/u/"] }, { "ID": 2, "Name": "双元音", "Words": ["/ei/", "/aɪ/", "/au/", "/əʊ/", "/ɔɪ/", "/ɪə/", "/ɛə/", "/uə/"] }, { "ID": 3, "Name": "清辅音", "Words": ["/p/", "/t/", "/k/", "/f/", "/θ/", "/s/", "/ts/", "/tr/", "/ʃ/", "/tʃ/", "/h/"] }, { "ID": 4, "Name": "浊辅音", "Words": ["/b/", "/d/", "/g/", "/r/", "/v/", "/ð/", "/z/", "/dz/", "/dr/", "/ʒ/", "/dʒ/", "/m/", "/n/", "/l/", "/ŋ/", "/j/", "/w/"] }];
  3944. for (const unit of result) {
  3945. unit.UnitName = unit.Name;
  3946. unit.ListStr = unit.Words.join("、");
  3947. unit.Length = unit.Words.length;
  3948. unit.TestFunction = info[0].TestFunction;
  3949. delete unit.Words;
  3950. }
  3951. arr.push(...result);
  3952. } else if (info[0].BookIDOld >= 111 && info[0].BookIDOld <= 119) {
  3953. const engList = await miaoguo.GetWordFrequencyList({ Limit: 3000 });
  3954. const arrTemp = [];
  3955. for (let i = info[0].StartID; i < info[0].StartID + info[0].WordNum; i++) {
  3956. if (i / 10 == Math.floor(i / 10)) {
  3957. arrTemp.push({
  3958. ID: Math.floor(i / 10) + 1,
  3959. UnitName: "单元" + (Math.floor(i / 10) + 1),
  3960. Words: [],
  3961. });
  3962. }
  3963. arrTemp[arrTemp.length - 1].Words.push(engList[i].Word);
  3964. }
  3965. for (const unit of arrTemp) {
  3966. unit.ListStr = unit.Words.join("、");
  3967. unit.Length = unit.Words.length;
  3968. unit.TestFunction = info[0].TestFunction;
  3969. delete unit.Words;
  3970. }
  3971. arr.push(...arrTemp);
  3972. } else if (["剑桥KET", "剑桥PET", "剑桥FCE"].includes(info[0].LibraryName1)) {
  3973. let sourceBookID = 133;
  3974. if (info[0].LibraryName1 == "剑桥PET")
  3975. sourceBookID = 134;
  3976. else if (info[0].LibraryName1 == "剑桥FCE")
  3977. sourceBookID = 136;
  3978. result = await hanzi.GetEnglishUnitData({
  3979. BookID: sourceBookID,
  3980. LessonID: info[0].BookIDOld,
  3981. });
  3982. arr.push(...buildCambridgeCategoryUnits(result, info[0].TestFunction));
  3983. } else {
  3984. const param2 = { BookID: info[0].BookIDOld };
  3985. result = await hanzi.GetEnglishUnitData(param2);
  3986. let unitNameStart = "Lesson ";
  3987. if (param2.BookID > 190 && param2.BookID < 207)
  3988. unitNameStart = "Unit ";
  3989. if (param2.BookID >= 207 && param2.BookID <= 211)
  3990. unitNameStart = "";
  3991. for (let i = 0; i < result.length; i++) {
  3992. if (temp && temp.LessonID != result[i].LessonID) {
  3993. item.ID = temp.LessonID;
  3994. item.UnitName = unitNameStart + temp.LessonID;
  3995. if (param2.BookID >= 207 && param2.BookID <= 211)
  3996. item.UnitName = temp.LessonName;
  3997. item.ListStr = item.List.join("、");
  3998. item.Length = item.List.length;
  3999. item.TestFunction = info[0].TestFunction;
  4000. delete item.List;
  4001. arr.push(item);
  4002. item = { List: [] };
  4003. }
  4004. item.List.push(result[i].Word);
  4005. temp = result[i];
  4006. }
  4007. if (result.length > 0) {
  4008. item.ID = result[result.length - 1].LessonID;
  4009. item.UnitName = unitNameStart + result[result.length - 1].LessonID;
  4010. if (param2.BookID >= 207 && param2.BookID <= 211)
  4011. item.UnitName = result[result.length - 1].LessonName;
  4012. item.ListStr = item.List.join("、");
  4013. item.Length = item.List.length;
  4014. item.TestFunction = info[0].TestFunction;
  4015. delete item.List;
  4016. arr.push(item);
  4017. }
  4018. }
  4019. result = arr;
  4020. }
  4021. result = {
  4022. BookInfo: info[0],
  4023. List1: result,
  4024. };
  4025. globalCache.set(url, result, config.BufferMemoryTimeHigh);
  4026. }
  4027. result.List2 = [];
  4028. if (param.UserID) {
  4029. param.IsNum = 1;
  4030. const list = await miaoguo.GetMiaoguoTestExamineReport2(param);
  4031. if (list && list.length > 0) {
  4032. for (const unit of result.List1) {
  4033. for (const report of list) {
  4034. if (unit.UnitName == report.UnitName) {
  4035. if (report.IsTestRead == 1)
  4036. unit.IsTestRead = 1;
  4037. if (report.IsBuildRead == 1)
  4038. unit.IsBuildRead = 1;
  4039. if (report.IsTestWrite == 1)
  4040. unit.IsTestWrite = 1;
  4041. if (report.IsBuildWrite == 1)
  4042. unit.IsBuildWrite = 1;
  4043. }
  4044. }
  4045. }
  4046. }
  4047. param.IsNum = 0;
  4048. result.List2 = await miaoguo.GetMiaoguoTestExamineReport2(param);
  4049. for (const item of result.List2) {
  4050. item.TestTime = moment(item.TestTime).format("YYYY年MM月DD日 HH:mm");
  4051. if (item.JSONData) {
  4052. const arr = JSON.parse(item.JSONData);
  4053. item.RightNum = arr[2].length;
  4054. item.WrongNum = arr[0].length + arr[1].length;
  4055. delete item.JSONData;
  4056. } else {
  4057. item.TestTime += " 已封存";
  4058. }
  4059. }
  4060. }
  4061. ctx.body = { errcode: 10000, result };
  4062. }
  4063. export async function InsertTestReport2(ctx) {
  4064. const param = ctx.request.body || {};
  4065. param.UserID = ctx.query.UserID || 0;
  4066. param.TestTime = moment().format("YYYY-MM-DD HH:mm:ss");
  4067. const list = await miaoguo.GetMiaoguoTestReport(param);
  4068. if (list && list.length > 0) {
  4069. param.ID = list[0].ID;
  4070. await miaoguo.UpdateMiaoguoTestReport(param);
  4071. } else {
  4072. await miaoguo.AddMiaoguoTestReport(param);
  4073. }
  4074. ctx.body = { errcode: 10000 };
  4075. }
  4076. export async function GetMiaoguoTestReportByID(ctx) {
  4077. const param = {
  4078. ID: ctx.query.ID || 0,
  4079. UserID: ctx.query.UserID || 0,
  4080. };
  4081. let result = null;
  4082. const info = await miaoguo.GetMiaoguoTestExamineReport2(param);
  4083. if (info && info.length > 0) {
  4084. result = info[0];
  4085. if (result.IsTestRead)
  4086. result.TestType = "read";
  4087. else if (result.IsTestWrite)
  4088. result.TestType = "write";
  4089. result.List = JSON.parse(result.JSONData);
  4090. delete result.JSONData;
  4091. result.TestTime = moment(result.TestTime).format("YYYY-MM-DD HH:mm:ss");
  4092. }
  4093. ctx.body = { errcode: 10000, result };
  4094. }
  4095. export async function InsertTestReport(ctx) {
  4096. const param = ctx.request.body || {};
  4097. param.UserID = ctx.query.UserID || 0;
  4098. if (param.ID) {
  4099. const param2 = {
  4100. ID: param.ID,
  4101. UserID: param.UserID,
  4102. TestRightStr: param.TestRightStr,
  4103. TestWrongStr: param.TestWrongStr,
  4104. TestSkipStr: param.TestSkipStr,
  4105. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  4106. };
  4107. if (param.TestTotal)
  4108. param2.TestTotal = param.TestTotal;
  4109. if (param.IsFinished == 1)
  4110. param2.IsFinished = 1;
  4111. if (param.IsFinished === 0)
  4112. param2.IsFinished = 0;
  4113. if (param.TestExistStr)
  4114. param2.TestExistStr = param.TestExistStr;
  4115. if (param.BookID)
  4116. param2.BookID = param.BookID;
  4117. await miaoguo.UpdateTestReport(param2);
  4118. } else {
  4119. param.UpdateTime = moment().format("YYYY-MM-DD HH:mm:ss");
  4120. const result = await miaoguo.InsertTestReport(param);
  4121. param.ID = result.insertId;
  4122. }
  4123. ctx.body = { errcode: 10000, result: param.ID };
  4124. }
  4125. export async function GetTestGradeArr(ctx) {
  4126. const bufferMemoryUrl = "GetTestGradeArr";
  4127. let result = globalCache.get(bufferMemoryUrl);
  4128. if (result === 0) {
  4129. const arr = [];
  4130. let param = { Type: "TestMenuMain" };
  4131. let listName = await hanzi.GetHanziBooks(param);
  4132. param.Type = "TestMenu";
  4133. let list = await hanzi.GetHanziBooks(param);
  4134. for (const nameItem of listName) {
  4135. const obj = {
  4136. Name: nameItem.Category,
  4137. Name2: nameItem.Category,
  4138. Type: "CHN",
  4139. Select: "",
  4140. List: [],
  4141. };
  4142. for (const item of list) {
  4143. if (nameItem.Category == item.Category) {
  4144. obj.List.push({
  4145. ID: item.ID,
  4146. Type: item.HanziType,
  4147. Name: item.Name1,
  4148. Name2: item.Name2,
  4149. Finish: 0,
  4150. Total: item.Total,
  4151. Image: item.Image,
  4152. });
  4153. }
  4154. }
  4155. arr.push(obj);
  4156. }
  4157. param = { FieldType: "Name1" };
  4158. listName = await miaoguo.GetWordBooks(param);
  4159. list = await miaoguo.GetWordBooks(null);
  4160. for (const nameItem of listName) {
  4161. const obj = {
  4162. Name: nameItem.Category,
  4163. Name2: nameItem.Category,
  4164. Type: "ENG",
  4165. Select: "",
  4166. List: [],
  4167. };
  4168. for (const item of list) {
  4169. if (nameItem.Category == item.Category) {
  4170. obj.List.push({
  4171. ID: item.ID,
  4172. Type: "Word",
  4173. Name: item.Name,
  4174. Name2: item.Name2,
  4175. Finish: 0,
  4176. Total: item.Total,
  4177. Image: item.Image,
  4178. });
  4179. }
  4180. }
  4181. arr.push(obj);
  4182. }
  4183. result = arr;
  4184. if (result)
  4185. globalCache.set(bufferMemoryUrl, result, config.BufferMemoryTimeHigher);
  4186. }
  4187. ctx.body = { errcode: 10000, result };
  4188. }
  4189. export async function GetTestReportList(ctx) {
  4190. const param = {
  4191. UserID: ctx.query.UserID || 0,
  4192. };
  4193. const list = await miaoguo.GetTestReportList(param);
  4194. for (const item of list) {
  4195. item.CreateTime = moment(item.CreateTime).format('MM月DD日');
  4196. item.UpdateTime = moment(item.UpdateTime).format('MM月DD日');
  4197. item.Name = item.Name.split("#");
  4198. if (item.wName1 && item.wFlag >= 0) {
  4199. item.Name[0] = item.wName1 + " " + item.wName2 + " " + item.wName3;
  4200. delete item.wName1;
  4201. delete item.wName2;
  4202. delete item.wName3;
  4203. delete item.wFlag;
  4204. }
  4205. if (item.hName1 && item.hFlag >= 0) {
  4206. item.Name[0] = item.hName1 + " " + item.hName2 + " " + item.hName3;
  4207. delete item.hName1;
  4208. delete item.hName2;
  4209. delete item.hName3;
  4210. delete item.hFlag;
  4211. }
  4212. }
  4213. ctx.body = { errcode: 10000, result: list };
  4214. }
  4215. export async function GetTestAncientPoetryList(ctx) {
  4216. const param = {
  4217. UserID: ctx.query.UserID || 0,
  4218. BookID: ctx.query.BookID || 0,
  4219. };
  4220. const list = await hanzi.GetAncientPoetryList(param);
  4221. const result = [];
  4222. for (const item of list) {
  4223. const peomContent = JSON.parse(item.PeomContent);
  4224. let str2 = item.Dynasty + " " + item.Author + "\n\n";
  4225. if (param.BookID >= 74 && item.Category == 1)
  4226. str2 = "\n\n";
  4227. for (const content of peomContent) {
  4228. if (stringUtils.IsArray(content)) {
  4229. for (const line of content)
  4230. str2 += line + "\n";
  4231. } else {
  4232. str2 += content + "\n";
  4233. }
  4234. }
  4235. let str3 = "";
  4236. if (item.Translation) {
  4237. const translation = JSON.parse(item.Translation);
  4238. for (const content of translation) {
  4239. if (stringUtils.IsArray(content)) {
  4240. for (const line of content)
  4241. str3 += line;
  4242. } else {
  4243. str3 += content;
  4244. }
  4245. }
  4246. }
  4247. const obj = {
  4248. ID: item.ID,
  4249. Word: item.Title,
  4250. Content: [],
  4251. };
  4252. obj.Content.push({ ContentType: 0, Content: item.Tag });
  4253. let str4 = "";
  4254. if (param.BookID >= 74 && item.Category == 1)
  4255. str4 = item.Title;
  4256. else
  4257. str4 = "《" + item.Title + "》";
  4258. if (item.ImgUrl)
  4259. str4 += "\n\n[图]" + item.ImgUrl + "[/图]";
  4260. obj.Content.push({ ContentType: 1, Content: str4 });
  4261. obj.Content.push({ ContentType: 2, Content: str2 });
  4262. obj.Content.push({ ContentType: 3, Content: str3 });
  4263. result.push(obj);
  4264. }
  4265. ctx.body = { errcode: 10000, result };
  4266. }
  4267. export async function GetTestEnglishWords(ctx) {
  4268. const param = {
  4269. UserID: ctx.query.UserID || 0,
  4270. Words: ctx.query.Words || "",
  4271. BookID: ctx.query.BookID || 0,
  4272. LessonID: ctx.query.LessonID || 0,
  4273. TestType: ctx.query.TestType || "read",
  4274. };
  4275. let list = [];
  4276. if (param.BookID && ((Number(param.BookID) > 120 && Number(param.BookID) < 169) || (Number(param.BookID) >= 184 && Number(param.BookID) <= 186) || (Number(param.BookID) > 190 && Number(param.BookID) <= 211))) {
  4277. list = await miaoguo.GetTestEnglishNewConceptWords(param);
  4278. for (const item of list) {
  4279. let question = "";
  4280. let answer = "";
  4281. let tags = "";
  4282. let soundmark = "";
  4283. const sound = item.Word.replace("ˈ", "%27");
  4284. let readString = "https://sp0.baidu.com/-rM1hT4a2gU2pMbgoY3K/gettts?lan=uk&text=" + sound + "&spd=3&source=alading";
  4285. if (item.Word == "our" || item.Word == "stomachache")
  4286. readString = readString.replace("lan=uk", "lan=en");
  4287. else if (item.Word == "Mr=mister")
  4288. readString = readString.replace("text=Mr=mister", "text=mister");
  4289. else if (item.Word == "(a) round the clock")
  4290. readString = readString.replace("text=(a) round the clock", "text=a round the clock");
  4291. else if (item.Word == "in favo(u)r of")
  4292. readString = readString.replace("text=in favo(u)r of", "text=in favor of");
  4293. if (param.TestType == "read") {
  4294. if ((param.BookID > 140 && param.BookID < 149) || (param.BookID > 160 && param.BookID < 165)) {
  4295. question = "[特]" + item.Word + "[/特]";
  4296. question += "\n\n\n\n" + item.ExampleSentence;
  4297. if (!item.Soundmark || item.Soundmark == "")
  4298. item.Soundmark = "发音";
  4299. soundmark = "[读 src='" + readString + "']" + item.Soundmark + "[/读]\n";
  4300. answer = soundmark + "\n\n";
  4301. answer += item.Translate;
  4302. tags = "发音,释义";
  4303. item.FontSize = 48;
  4304. } else {
  4305. question = item.Word;
  4306. if (!item.Soundmark || item.Soundmark == "")
  4307. item.Soundmark = "发音";
  4308. soundmark = "[读 src='" + readString + "']" + item.Soundmark + "[/读]\n";
  4309. answer = soundmark + "\n\n";
  4310. if (param.BookID > 190 && param.BookID <= 211)
  4311. answer += item.Translate;
  4312. else
  4313. answer += "[光]" + item.Translate + "[/光]";
  4314. tags = "发音,释义";
  4315. item.FontSize = 108;
  4316. if (param.BookID >= 199 && param.BookID < 207)
  4317. item.FontSize = 54;
  4318. }
  4319. } else if (param.TestType == "write") {
  4320. if ((param.BookID > 140 && param.BookID < 149) || (param.BookID > 160 && param.BookID < 165)) {
  4321. soundmark = "[读 src='" + readString + "']发音[/读]\n";
  4322. question = soundmark + "\n\n";
  4323. question += item.Translate;
  4324. answer = "[特]" + item.Word + "[/特]";
  4325. answer += "\n\n\n\n" + item.ExampleSentence;
  4326. tags = "默写单词";
  4327. item.FontSize = 48;
  4328. } else {
  4329. soundmark = "[读 src='" + readString + "']发音[/读]\n";
  4330. question = soundmark + "\n\n";
  4331. question += item.Translate;
  4332. if (param.BookID == 135 || param.BookID == 184 || param.BookID == 185 || param.BookID == 186)
  4333. tags = "默写词组";
  4334. if (param.BookID >= 199 && param.BookID < 207)
  4335. tags = "默写句子";
  4336. else
  4337. tags = "默写单词";
  4338. answer = "[特]" + item.Word + "[/特]";
  4339. if (param.BookID >= 199 && param.BookID < 207)
  4340. answer = item.Word;
  4341. item.FontSize = 48;
  4342. }
  4343. }
  4344. item.ReadString = readString;
  4345. item.Content = [
  4346. { ContentType: 0, Content: tags },
  4347. { ContentType: 1, Content: question },
  4348. { ContentType: 2, Content: answer },
  4349. { ContentType: 3, Content: "" },
  4350. ];
  4351. delete item.Soundmark;
  4352. delete item.Translate;
  4353. }
  4354. } else if (param.BookID && Number(param.BookID) > 100 && Number(param.BookID) < 110) {
  4355. list = constantClass.GetPhonetic();
  4356. const arr = [];
  4357. for (const item of list) {
  4358. if (item.LessonID == param.LessonID)
  4359. arr.push(item);
  4360. }
  4361. list = arr;
  4362. } else if ((Number(param.BookID) >= 169 && Number(param.BookID) <= 183) || (Number(param.BookID) >= 212 && Number(param.BookID) <= 217)) {
  4363. if (Number(param.BookID) >= 169 && Number(param.BookID) <= 172)
  4364. param.Type = "GaoKao";
  4365. else if (Number(param.BookID) >= 173 && Number(param.BookID) <= 177)
  4366. param.Type = "GET4";
  4367. else if (Number(param.BookID) >= 178 && Number(param.BookID) <= 183)
  4368. param.Type = "GET6";
  4369. else if (Number(param.BookID) >= 212 && Number(param.BookID) <= 217)
  4370. param.Type = "GET6";
  4371. list = await miaoguo.GetTestEnglishWords(param);
  4372. const listTextbook = await miaoguo.GetTestEnglishWords({
  4373. ...param,
  4374. Type: "textbook",
  4375. Words: list.map(item => item.Word),
  4376. });
  4377. for (const item of list) {
  4378. if (!item.JSONString) {
  4379. const soundFallback = item.Word.replace("ˈ", "%27");
  4380. const readStringFallback = "https://sp0.baidu.com/-rM1hT4a2gU2pMbgoY3K/gettts?lan=uk&text=" + soundFallback + "&spd=3&source=alading";
  4381. if (param.TestType == "write") {
  4382. item.Content = [
  4383. { ContentType: 0, Content: "默写单词" },
  4384. { ContentType: 1, Content: "[读 src='" + readStringFallback + "']发音[/读]\n\n[光]" + item.Translate + "[/光]" },
  4385. { ContentType: 2, Content: "[特]" + item.Word + "[/特]" },
  4386. { ContentType: 3, Content: "" },
  4387. ];
  4388. item.FontSize = 48;
  4389. } else {
  4390. item.Content = [
  4391. { ContentType: 0, Content: "发音,释义" },
  4392. { ContentType: 1, Content: item.Word },
  4393. { ContentType: 2, Content: "[读 src='" + readStringFallback + "']" + (item.Soundmark || "发音") + "[/读]\n\n[光]" + item.Translate + "[/光]" },
  4394. { ContentType: 3, Content: "" },
  4395. ];
  4396. item.FontSize = 108;
  4397. }
  4398. item.ReadString = readStringFallback;
  4399. continue;
  4400. }
  4401. const json = JSON.parse(item.JSONString);
  4402. item.Data = json.ENG;
  4403. let question = "";
  4404. let answer = "";
  4405. let tags = "";
  4406. let soundmark = "";
  4407. let paraphrase = "";
  4408. if (item.Data.Soundmark && item.Data.Soundmark.EngSound && item.Data.Soundmark.Eng)
  4409. soundmark = "[读 src='" + item.Data.Soundmark.EngSound + "']" + item.Data.Soundmark.Eng + "[/读]\n \n";
  4410. if (item.Data.Paraphrase && item.Data.Paraphrase.length > 0) {
  4411. for (const p of item.Data.Paraphrase) {
  4412. paraphrase += p.PartOfSpeech;
  4413. paraphrase += " " + p.ParaphraseList.join(" ") + "\n\n";
  4414. }
  4415. }
  4416. let paraphraseBasic = "";
  4417. let paraphraseBasicEng = "";
  4418. if (listTextbook && listTextbook.length > 0) {
  4419. for (const textBook of listTextbook) {
  4420. if (textBook.Word == item.Data.Word)
  4421. paraphraseBasicEng = textBook.Translate;
  4422. }
  4423. }
  4424. paraphraseBasic = item.Translate;
  4425. if (param.Words.substr(0, 8) == "\"A\",\"B\",") {
  4426. question = item.Data.Word + " " + item.Data.Word.toString().toLowerCase();
  4427. answer = soundmark.substr(0, soundmark.length - 3);
  4428. answer += "[图]" + json.CHN.BiShunArr2[0] + "[/图]";
  4429. answer += "\n[图]" + json.CHN.BiShunArr2[1] + "[/图]";
  4430. tags = "发音,书写";
  4431. item.FontSize = 108;
  4432. } else if (param.TestType == "read") {
  4433. question = item.Data.Word;
  4434. answer = soundmark;
  4435. if (paraphraseBasic) {
  4436. answer += item.Data.Word + " 的基本中文释义\n\n";
  4437. answer += "[光]" + paraphraseBasic + "[/光]\n\n";
  4438. }
  4439. if (paraphraseBasicEng) {
  4440. answer += item.Data.Word + " 的基本英文释义\n\n";
  4441. answer += "[光]" + paraphraseBasicEng + "[/光]\n\n";
  4442. }
  4443. if (Number(param.BookID) >= 169 && Number(param.BookID) <= 172) {
  4444. answer += item.Data.Word + " 的更多中文释义\n\n";
  4445. answer += paraphrase;
  4446. }
  4447. tags = "发音,释义";
  4448. item.FontSize = 108;
  4449. } else if (param.TestType == "write") {
  4450. if (item.Data.Soundmark && item.Data.Soundmark.Eng)
  4451. soundmark = soundmark.replace(item.Data.Soundmark.Eng, "发音");
  4452. else
  4453. soundmark = "";
  4454. question = soundmark;
  4455. if (paraphraseBasic)
  4456. question += "[光]" + paraphraseBasic + "[/光]\n\n";
  4457. if (paraphraseBasicEng)
  4458. question += "[光]" + paraphraseBasicEng + "[/光]\n\n";
  4459. if (Number(param.BookID) >= 169 && Number(param.BookID) <= 172) {
  4460. paraphrase = stringUtils.ReplaceAllString(paraphrase, item.Data.Word, "[光]~[/光]");
  4461. question += paraphrase;
  4462. }
  4463. answer = "[特]" + item.Data.Word + "[/特]";
  4464. tags = "默写单词";
  4465. item.FontSize = 48;
  4466. }
  4467. item.Word = item.Data.Word;
  4468. if (item.Data.Soundmark && item.Data.Soundmark.EngSound)
  4469. item.ReadString = item.Data.Soundmark.EngSound;
  4470. else
  4471. item.ReadString = "";
  4472. item.Content = [
  4473. { ContentType: 0, Content: tags },
  4474. { ContentType: 1, Content: question },
  4475. { ContentType: 2, Content: answer },
  4476. { ContentType: 3, Content: "" },
  4477. ];
  4478. delete item.JSONString;
  4479. delete item.Data;
  4480. }
  4481. } else {
  4482. param.Words = param.Words.split(",");
  4483. param.Words = JSON.stringify(param.Words);
  4484. param.Words = param.Words.substr(1, param.Words.length - 2);
  4485. list = await miaoguo.GetTestEnglishWords(param);
  4486. param.Type = "textbook";
  4487. const listTextbook = await miaoguo.GetTestEnglishWords(param);
  4488. for (const item of list) {
  4489. const json = JSON.parse(item.JSONString);
  4490. item.Data = json.ENG;
  4491. let question = "";
  4492. let answer = "";
  4493. let tags = "";
  4494. let soundmark = "";
  4495. let paraphrase = "";
  4496. if (item.Data.Soundmark && item.Data.Soundmark.EngSound && item.Data.Soundmark.Eng)
  4497. soundmark = "[读 src='" + item.Data.Soundmark.EngSound + "']" + item.Data.Soundmark.Eng + "[/读]\n \n";
  4498. if (item.Data.Paraphrase && item.Data.Paraphrase.length > 0) {
  4499. for (const p of item.Data.Paraphrase) {
  4500. paraphrase += p.PartOfSpeech;
  4501. paraphrase += " " + p.ParaphraseList.join(" ") + "\n\n";
  4502. }
  4503. }
  4504. let paraphraseBasic = "";
  4505. let paraphraseBasicEng = "";
  4506. let exampleSentence = "";
  4507. if (listTextbook && listTextbook.length > 0) {
  4508. for (const textBook of listTextbook) {
  4509. if (textBook.Word == item.Data.Word && textBook.BookID < 160)
  4510. paraphraseBasic = textBook.Translate;
  4511. if (textBook.Word == item.Data.Word && textBook.BookID > 160) {
  4512. paraphraseBasicEng = textBook.Translate;
  4513. exampleSentence = textBook.ExampleSentence;
  4514. }
  4515. }
  4516. }
  4517. if (param.Words.substr(0, 8) == "\"A\",\"B\",") {
  4518. question = item.Data.Word + " " + item.Data.Word.toString().toLowerCase();
  4519. answer = soundmark.substr(0, soundmark.length - 3);
  4520. answer += "[图]" + json.CHN.BiShunArr2[0] + "[/图]";
  4521. answer += "\n[图]" + json.CHN.BiShunArr2[1] + "[/图]";
  4522. tags = "发音,书写";
  4523. item.FontSize = 108;
  4524. } else if (param.TestType == "read") {
  4525. question = item.Data.Word;
  4526. answer = soundmark;
  4527. if (paraphraseBasic) {
  4528. answer += item.Data.Word + " 的基本中文释义\n\n";
  4529. answer += "[光]" + paraphraseBasic + "[/光]\n\n";
  4530. }
  4531. if (paraphraseBasicEng) {
  4532. answer += item.Data.Word + " 的基本英文释义\n\n";
  4533. answer += "[光]" + paraphraseBasicEng + "[/光]\n\n";
  4534. }
  4535. answer += item.Data.Word + " 的更多中文释义\n\n";
  4536. answer += paraphrase;
  4537. tags = "发音,释义";
  4538. item.FontSize = 108;
  4539. } else if (param.TestType == "write") {
  4540. if (item.Data.Soundmark && item.Data.Soundmark.Eng)
  4541. soundmark = soundmark.replace(item.Data.Soundmark.Eng, "发音");
  4542. else
  4543. soundmark = "";
  4544. question = soundmark;
  4545. if (paraphraseBasic)
  4546. question += "[光]" + paraphraseBasic + "[/光]\n\n";
  4547. if (paraphraseBasicEng)
  4548. question += "[光]" + paraphraseBasicEng + "[/光]\n\n";
  4549. paraphrase = stringUtils.ReplaceAllString(paraphrase, item.Data.Word, "[光]~[/光]");
  4550. question += paraphrase;
  4551. answer = "[特]" + item.Data.Word + "[/特]";
  4552. tags = "默写单词";
  4553. item.FontSize = 48;
  4554. }
  4555. item.Word = item.Data.Word;
  4556. if (item.Data.Soundmark && item.Data.Soundmark.EngSound)
  4557. item.ReadString = item.Data.Soundmark.EngSound;
  4558. else
  4559. item.ReadString = "";
  4560. item.Content = [
  4561. { ContentType: 0, Content: tags },
  4562. { ContentType: 1, Content: question },
  4563. { ContentType: 2, Content: answer },
  4564. { ContentType: 3, Content: "" },
  4565. ];
  4566. delete item.JSONString;
  4567. delete item.Data;
  4568. if (param.TestType == "read" && param.Words.substr(0, 8) != "\"A\",\"B\",") {
  4569. if (exampleSentence || (json.ENG.ExamplesSentences && json.ENG.ExamplesSentences.length > 0)) {
  4570. const temp = json.ENG.ExamplesSentences;
  4571. item.Content[2].Content = item.Content[2].Content + "\n例句:";
  4572. let index = 0;
  4573. if (exampleSentence)
  4574. item.Content[2].Content = item.Content[2].Content + "\n\n(" + (++index) + ") " + exampleSentence + "\n\n";
  4575. const len = temp.length > 5 ? 5 : temp.length;
  4576. for (let j = 0; j < len; j++) {
  4577. const num = "(" + (++index) + ")";
  4578. item.Content[2].Content = item.Content[2].Content + "\n\n" + num + " " + temp[j][0] + "\n" + temp[j][1];
  4579. }
  4580. }
  4581. }
  4582. }
  4583. }
  4584. ctx.body = { errcode: 10000, result: list };
  4585. }
  4586. export async function GetMiaoguoTestExamine(ctx) {
  4587. const param = {
  4588. BookID: ctx.query.BookID || 0,
  4589. UnitName: ctx.query.UnitName || "",
  4590. UnitID: ctx.query.UnitID || 0,
  4591. TestType: ctx.query.TestType || "read",
  4592. IsBuild: ctx.query.IsBuild || false,
  4593. };
  4594. const cacheKey = "GetMiaoguoTestExamine?BookID=" + param.BookID + "&UnitName=" + param.UnitName + "&UnitID=" + param.UnitID + "&TestType=" + param.TestType + "&IsBuild=" + param.IsBuild;
  4595. let result = globalCache.get(cacheKey);
  4596. if (result === 0) {
  4597. const info = await miaoguo.GetMiaoguoPackage(param);
  4598. param.Category = info[0].Category;
  4599. param.BookID = info[0].BookIDOld;
  4600. if (param.Category == "语文") {
  4601. if (param.BookID == "43" || param.BookID == "44") {
  4602. const list = await fetchLocalResult('/api/GetPinyinUnitWords?TestType=' + param.TestType + '&BookID=' + param.BookID + '&UnitID=' + param.UnitID + '&Word=' + param.UnitName);
  4603. const arr = [];
  4604. for (const item of list) {
  4605. const obj = {};
  4606. if (param.IsBuild)
  4607. obj.Content = item.Content;
  4608. obj.ContentNew = changeStringToView(item.Content);
  4609. obj.TagWidth = 230;
  4610. obj.FontSize = 108;
  4611. obj.Word = item.Word;
  4612. obj.ReadString = item.ReadString;
  4613. arr.push(obj);
  4614. }
  4615. result = arr;
  4616. } else if (param.UnitName == "古诗文" || param.UnitName == "日积月累") {
  4617. const list = await fetchLocalResult('/api/GetTestAncientPoetryList?BookID=' + param.BookID);
  4618. const arr = [];
  4619. for (const item of list) {
  4620. const obj = {};
  4621. if (param.IsBuild)
  4622. obj.Content = item.Content;
  4623. obj.ContentNew = changeStringToView(item.Content);
  4624. obj.TagWidth = 306;
  4625. if (param.BookID >= "74")
  4626. obj.TagWidth = 520;
  4627. if (item.Word.length > 8)
  4628. obj.FontSize = 48;
  4629. else
  4630. obj.FontSize = 64;
  4631. obj.Word = item.Word;
  4632. arr.push(obj);
  4633. }
  4634. result = arr;
  4635. } else {
  4636. const unitInfo = await hanzi.GetHanziUnitInfo(param);
  4637. if (unitInfo && unitInfo.length > 0 && unitInfo[0].UnitType == 2) {
  4638. const words = await hanzi.GetHanziUnitByID(param);
  4639. if (words[0] && words[0].Example) {
  4640. param.Words = words[0].Example.split(",").join("','");
  4641. result = await hanzi.GetMiaoguoLiteracyWords(param);
  4642. let temp = "";
  4643. const arrTemp = [];
  4644. for (const item of result) {
  4645. if (temp != item.Word && item.JSONString) {
  4646. const json = JSON.parse(item.JSONString);
  4647. const obj = { ID: arrTemp.length + 1 };
  4648. let qustion;
  4649. let answer;
  4650. let tags;
  4651. if (param.TestType == "read") {
  4652. qustion = item.Word;
  4653. answer = "[读 src='" + item.Word + "']" + json.CHN.PinYin[0].pinyin + "[/读]\n\n";
  4654. answer += json.CHN.PinYin[0].explain;
  4655. tags = "怎么念";
  4656. obj.FontSize = 108;
  4657. } else {
  4658. qustion = "[读 src='" + item.Word + "']" + json.CHN.PinYin[0].pinyin + "[/读]\n";
  4659. answer = item.Word + "\n\n";
  4660. if (json.CHN.KaitiArr) {
  4661. for (const kaiti of json.CHN.KaitiArr)
  4662. answer += "[图 w='650' h='650']" + kaiti + "[/图]\n";
  4663. }
  4664. answer += json.CHN.PinYin[0].explain;
  4665. tags = "怎么写";
  4666. }
  4667. obj.Word = item.Word;
  4668. obj.ReadString = item.Word;
  4669. obj.Content = [
  4670. { ContentType: 0, Content: tags },
  4671. { ContentType: 1, Content: qustion },
  4672. { ContentType: 2, Content: answer },
  4673. { ContentType: 3, Content: "" },
  4674. ];
  4675. obj.ContentNew = changeStringToView(obj.Content);
  4676. obj.TagWidth = 136;
  4677. if (!param.IsBuild)
  4678. delete obj.Content;
  4679. arrTemp.push(obj);
  4680. }
  4681. temp = item.Word;
  4682. }
  4683. result = arrTemp;
  4684. }
  4685. } else if (unitInfo && unitInfo.length > 0 && unitInfo[0].UnitType == 4) {
  4686. const words = await hanzi.GetHanziUnitByID(param);
  4687. const arrAncientList = await hanzi.GetAncientChineseProse({ IsList: 0 });
  4688. result = [];
  4689. if (words[0] && words[0].Name) {
  4690. for (const ancient of arrAncientList) {
  4691. if (ancient.Word == words[0].Name) {
  4692. let qustion = ancient.ExampleSentence;
  4693. qustion = stringUtils.ReplaceAllString(qustion, ancient.Word, "[光]" + ancient.Word + "[/光]");
  4694. const readString = "“" + ancient.Word + "”字在这里的意思是" + ancient.Definitions;
  4695. let answer = "[读 src='" + readString + "']" + ancient.Definitions + "[/读]\n\n";
  4696. if (ancient.ExampleDefinitions.indexOf("译文") < 0)
  4697. answer += "译文:";
  4698. answer += ancient.ExampleDefinitions;
  4699. const obj = {
  4700. Word: "【" + ancient.Word + "】" + ancient.Definitions,
  4701. ReadString: readString,
  4702. Content: [
  4703. { ContentType: 0, Content: "文言文字释义" },
  4704. { ContentType: 1, Content: qustion },
  4705. { ContentType: 2, Content: answer },
  4706. { ContentType: 3, Content: "" },
  4707. ],
  4708. TagWidth: 270,
  4709. };
  4710. obj.ContentNew = changeStringToView(obj.Content);
  4711. if (!param.IsBuild)
  4712. delete obj.Content;
  4713. result.push(obj);
  4714. }
  4715. }
  4716. }
  4717. } else if (unitInfo && unitInfo.length > 0 && unitInfo[0].UnitType == 3) {
  4718. const words = await hanzi.GetHanziUnitByID(param);
  4719. const resultList = await hanzi.GetAncientChineseProseSentence({ Unit: words[0].Name });
  4720. result = [];
  4721. if (words[0] && words[0].Name) {
  4722. for (const item of resultList) {
  4723. let qustion;
  4724. let answer;
  4725. let tags;
  4726. if (param.TestType == "read") {
  4727. qustion = item.Question;
  4728. answer = "[光]" + item.Sentence + "[/光]\n\n";
  4729. answer += "含义\n• " + item.Paraphrase + "\n\n";
  4730. if (item.Explain)
  4731. answer += "详解\n• " + item.Explain + "\n\n";
  4732. if (item.Idiom)
  4733. answer += "句中包含的成语\n• " + item.Idiom + "\n\n";
  4734. if (item.SentenceFragment)
  4735. answer += "原文片段\n• " + item.SentenceFragment + "\n\n";
  4736. if (item.Source) {
  4737. answer += "出自\n• 《" + item.Source + "》 ";
  4738. if (item.Author)
  4739. answer += item.Author + " ";
  4740. if (item.Dynasty)
  4741. answer += "(" + item.Dynasty + ")";
  4742. answer += "\n\n";
  4743. }
  4744. tags = "接下文";
  4745. } else {
  4746. qustion = item.Paraphrase;
  4747. answer = "[光]" + item.Sentence + "[/光]\n\n";
  4748. if (item.Explain)
  4749. answer += "详解\n• " + item.Explain + "\n\n";
  4750. if (item.Idiom)
  4751. answer += "句中包含的成语\n• " + item.Idiom + "\n\n";
  4752. if (item.SentenceFragment)
  4753. answer += "原文片段\n• " + item.SentenceFragment + "\n\n";
  4754. if (item.Source) {
  4755. answer += "出自\n• 《" + item.Source + "》 ";
  4756. if (item.Author)
  4757. answer += item.Author + " ";
  4758. if (item.Dynasty)
  4759. answer += "(" + item.Dynasty + ")";
  4760. answer += "\n\n";
  4761. }
  4762. tags = "说古文";
  4763. }
  4764. const obj = {
  4765. Word: item.Sentence,
  4766. Content: [
  4767. { ContentType: 0, Content: tags },
  4768. { ContentType: 1, Content: qustion },
  4769. { ContentType: 2, Content: answer },
  4770. { ContentType: 3, Content: "" },
  4771. ],
  4772. TagWidth: 136,
  4773. };
  4774. obj.ContentNew = changeStringToView(obj.Content);
  4775. if (!param.IsBuild)
  4776. delete obj.Content;
  4777. result.push(obj);
  4778. }
  4779. }
  4780. } else {
  4781. if ((param.UnitID > 93 && param.UnitID <= 358) || (param.UnitID >= 561)) {
  4782. const words = await hanzi.GetHanziUnitByID(param);
  4783. param.Words = words[0].Example.split("").join("','");
  4784. result = await hanzi.GetHanziUnitWords(param);
  4785. } else if (param.UnitID <= 93) {
  4786. result = await hanzi.GetHanziUnitWords(param);
  4787. }
  4788. for (const item of result) {
  4789. if (item.Pinyin) {
  4790. item.Pinyin = item.Pinyin.split(",")[0];
  4791. item.CombineWords2 = stringUtils.ReplaceAllString(item.CombineWords, item.Name, item.Pinyin);
  4792. if (item.CombineWords.length == 2 && item.CombineWords[0] == item.CombineWords[1]) {
  4793. switch (item.CombineWords) {
  4794. case "妈妈":
  4795. item.CombineWords2 = "māma";
  4796. break;
  4797. case "爸爸":
  4798. item.CombineWords2 = "bàba";
  4799. break;
  4800. case "叔叔":
  4801. item.CombineWords2 = "shūshu";
  4802. break;
  4803. case "弟弟":
  4804. item.CombineWords2 = "dìdi";
  4805. break;
  4806. case "爷爷":
  4807. item.CombineWords2 = "yéye";
  4808. break;
  4809. case "哥哥":
  4810. item.CombineWords2 = "gēge";
  4811. break;
  4812. case "玲玲":
  4813. item.CombineWords2 = "língling";
  4814. break;
  4815. }
  4816. }
  4817. if (item.CombineWords.length == 2) {
  4818. switch (item.CombineWords) {
  4819. case "倔强":
  4820. item.CombineWords2 = "jué强";
  4821. item.Pinyin = "jué";
  4822. break;
  4823. case "搂住":
  4824. item.CombineWords2 = "lǒu住";
  4825. item.Pinyin = "lǒu";
  4826. break;
  4827. }
  4828. }
  4829. }
  4830. }
  4831. result = getWordJSON(param, result);
  4832. }
  4833. }
  4834. } else if (param.Category == "English") {
  4835. let unitsid = "";
  4836. let words = "";
  4837. const bookid = param.BookID;
  4838. if (bookid > 100 && bookid < 110 && param.UnitName)
  4839. unitsid = Number(param.UnitName);
  4840. else if (bookid >= 111 && bookid <= 119) {
  4841. const engList = await miaoguo.GetWordFrequencyList({ Limit: 3000 });
  4842. const idStart = (Number(param.UnitName.replace("单元", "")) - 1) * 10;
  4843. words = "&Words=";
  4844. for (let j = idStart; j < idStart + 10; j++)
  4845. words += engList[j].Word + ",";
  4846. words = words.substr(0, words.length - 1);
  4847. } else if ((bookid > 120 && bookid <= 124) || (bookid >= 169 && bookid <= 186))
  4848. unitsid = Number(param.UnitName.replace("Lesson ", ""));
  4849. else if (bookid > 130 && bookid <= 168 && param.UnitName)
  4850. unitsid = Number(param.UnitName);
  4851. else if (bookid == 100) {
  4852. param.TestType = "read";
  4853. words = "&Words=A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
  4854. } else if (info[0].LibraryName1.indexOf("剑桥") >= 0 && info[0].LibraryName2 == "分类词汇") {
  4855. unitsid = param.UnitID;
  4856. param.UnitID = param.BookID;
  4857. param.BookID = 133;
  4858. if (info[0].LibraryName1 == "剑桥PET")
  4859. param.BookID = 134;
  4860. else if (info[0].LibraryName1 == "剑桥FCE")
  4861. param.BookID = 136;
  4862. } else if (bookid > 190 && bookid <= 211)
  4863. unitsid = param.UnitID;
  4864. const list = await fetchLocalResult('/api/GetTestEnglishWords?TestType=' + param.TestType + '&BookID=' + param.BookID + '&LessonID=' + param.UnitID + words);
  4865. const arr = [];
  4866. for (let i = 0; i < list.length; i++) {
  4867. const obj = {};
  4868. if (param.IsBuild)
  4869. obj.Content = list[i].Content;
  4870. if (list[i].ExampleSentence && list[i].ExampleSentence.substring(0, 1) == "{") {
  4871. const arrTemp = ["\n\n例句:"];
  4872. const jsonSentence = JSON.parse(list[i].ExampleSentence);
  4873. const arrSentence = jsonSentence.Sentences;
  4874. const count = arrSentence.length > 2 ? 2 : arrSentence.length;
  4875. for (let k = 0; k < count; k++) {
  4876. let sentence1 = arrSentence[k].Sentence;
  4877. const word2 = list[i].Word;
  4878. sentence1 = stringUtils.ReplaceAllString(sentence1, word2, "[线]" + word2 + "[/线]");
  4879. if (sentence1.substring(0, word2.length).toLowerCase() == word2.toLowerCase()) {
  4880. const word1 = sentence1.substring(0, word2.length);
  4881. sentence1 = sentence1.replace(word1, "[线]" + word1 + "[/线]");
  4882. }
  4883. arrTemp.push(sentence1);
  4884. arrTemp.push(arrSentence[k].Translate);
  4885. arrTemp.push("\n");
  4886. }
  4887. list[i].Content[2].Content += arrTemp.join("\n");
  4888. }
  4889. obj.ContentNew = changeStringToView(list[i].Content);
  4890. obj.Word = list[i].Word;
  4891. obj.ReadString = list[i].ReadString;
  4892. if (param.TestType == "read") {
  4893. obj.TagWidth = 212;
  4894. obj.FontSize = 108;
  4895. if ((param.BookID >= 140 && param.BookID <= 149) || (param.BookID >= 160 && param.BookID <= 164))
  4896. obj.FontSize = 48;
  4897. else if ((param.BookID == 135) || (param.BookID >= 184 && param.BookID <= 186))
  4898. obj.FontSize = 72;
  4899. if (obj.Word.length > 10)
  4900. obj.FontSize = 84;
  4901. if (param.BookID >= 199 && param.BookID < 207)
  4902. obj.FontSize = 54;
  4903. } else if (param.TestType == "write") {
  4904. obj.TagWidth = 186;
  4905. obj.FontSize = 42;
  4906. }
  4907. if (info[0].LibraryName1.indexOf("剑桥") >= 0 && info[0].LibraryName2 == "分类词汇") {
  4908. if (i >= (unitsid - 1) * 10 && i < unitsid * 10)
  4909. arr.push(obj);
  4910. } else {
  4911. arr.push(obj);
  4912. }
  4913. }
  4914. result = arr;
  4915. }
  4916. globalCache.set(cacheKey, result, config.BufferMemoryTimeHigh);
  4917. }
  4918. ctx.body = { errcode: 10000, result };
  4919. }
  4920. export async function AddMiaoguoCard(ctx) {
  4921. const param = ctx.request.body || {};
  4922. let createSource = "app";
  4923. if (param.VisitType) {
  4924. createSource = Decrypt(param.VisitType, config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
  4925. if (createSource == "web") {
  4926. // 新增卡片时客户端传原始 ID(如 0)而非加密串,解密会失败;
  4927. // 解密失败时保留原值,新增卡走 Type=add 分支即可。
  4928. if (param.ID) {
  4929. try {
  4930. param.ID = Decrypt(param.ID, config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
  4931. } catch (e) {
  4932. // ID 未加密(新增卡),保持原值
  4933. }
  4934. }
  4935. delete param.VisitType;
  4936. ctx.query.Type = param.Type;
  4937. delete param.Type;
  4938. }
  4939. }
  4940. param.UserID = ctx.query.UserID;
  4941. const field1 = param.Field1 || "";
  4942. const field2 = param.Field2 || "";
  4943. const field3 = param.Field3 || "";
  4944. let tags = "";
  4945. if (param.Tags && param.Tags.length > 0)
  4946. tags = stringUtils.IsArray(param.Tags) ? param.Tags.join(",") : param.Tags;
  4947. delete param.Field1;
  4948. delete param.Field2;
  4949. delete param.Field3;
  4950. delete param.Tags;
  4951. let isExist = false;
  4952. if (ctx.query.Type == "IsExist") {
  4953. const param2 = {
  4954. UserID: ctx.query.UserID,
  4955. FolderID: param.FolderID,
  4956. Content: stringUtils.ReplaceAllString(field1, "'", "''"),
  4957. IsTest: 1,
  4958. };
  4959. param2.Content = stringUtils.ReplaceAllString(param2.Content, "\n", "");
  4960. const miaoguoCardID = await miaoguo.GetCardIDByContent(param2);
  4961. if (miaoguoCardID && miaoguoCardID.length > 0) {
  4962. param2.MiaoguoCardID = miaoguoCardID[0].MiaoguoCardID;
  4963. param2.Type = "TestReset";
  4964. await miaoguo.ResetCard(param2);
  4965. globalCache.delete("GetMiaoguoCard###UserID=" + param.UserID);
  4966. isExist = true;
  4967. }
  4968. }
  4969. if (!isExist && (ctx.query.Type == "edit" || ctx.query.Type == "edit1" || ctx.query.Type == "edit2" || ctx.query.Type == "edit3")) {
  4970. param.MiaoguoCardID = param.ID;
  4971. delete param.ID;
  4972. let obj = {
  4973. MiaoguoCardID: param.MiaoguoCardID,
  4974. ContentType: 1,
  4975. Content: updateSoundError(field1, createSource),
  4976. };
  4977. await miaoguo.UpdateCardContent(obj);
  4978. obj.ContentType = 2;
  4979. obj.Content = updateSoundError(field2, createSource);
  4980. await miaoguo.UpdateCardContent(obj);
  4981. obj.ContentType = 3;
  4982. obj.Content = updateSoundError(field3, createSource);
  4983. await miaoguo.UpdateCardContent(obj);
  4984. if (tags || tags.length == 0) {
  4985. obj.ContentType = 0;
  4986. obj.Content = tags;
  4987. await miaoguo.UpdateCardContent(obj);
  4988. }
  4989. if (param.IsTodayStudy || param.LimitTime) {
  4990. obj = {
  4991. MiaoguoCardID: param.MiaoguoCardID,
  4992. IsTodayStudy: param.IsTodayStudy,
  4993. IsCollect: param.IsCollect || 0,
  4994. FolderID: param.FolderID || 0,
  4995. FontSize: param.FontSize || 48,
  4996. };
  4997. if (param.LimitTime) {
  4998. if (param.LimitTime.indexOf("/") >= 0)
  4999. param.LimitTime = stringUtils.ReplaceAllString(param.LimitTime.toString(), "/", "-");
  5000. obj.LimitTime = moment(param.LimitTime).format('YYYY-MM-DD HH:mm:ss');
  5001. }
  5002. if (param.IsTodayStudy == 1)
  5003. obj.LimitTime = moment().format('YYYY-MM-DD HH:mm:ss');
  5004. obj.UpdateTime = moment().format('YYYY-MM-DD HH:mm:ss');
  5005. const cardInfo = await miaoguo.GetCardInfo(param);
  5006. await miaoguo.UpdateCard(obj);
  5007. const param2 = {
  5008. UserID: param.UserID,
  5009. MiaoguoCardID: param.MiaoguoCardID,
  5010. CardType: param.CardType || 0,
  5011. LimitTime: obj.LimitTime,
  5012. };
  5013. if (param2.LimitTime === "" || param2.LimitTime == "undefined")
  5014. delete param2.LimitTime;
  5015. else if (cardInfo && cardInfo.length > 0) {
  5016. const limittime = moment(cardInfo[0].LimitTime).format('YYYY-MM-DD HH:mm:ss');
  5017. if (param2.LimitTime == limittime)
  5018. delete param2.LimitTime;
  5019. }
  5020. if (cardInfo && cardInfo.length > 0 && param2.CardType != cardInfo[0].CardType)
  5021. await miaoguo.UpdateCardType(param2);
  5022. miaoguo.AddMiaoguoLog({
  5023. UserID: ctx.query.UserID,
  5024. LogID: 298,
  5025. Content: JSON.stringify({ Content1: obj, Content2: param2 }),
  5026. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  5027. }
  5028. } else if (!isExist) {
  5029. delete param.ID;
  5030. param.CreateTime = moment().format('YYYY-MM-DD HH:mm:ss');
  5031. if (!param.LimitTime)
  5032. param.LimitTime = param.CreateTime;
  5033. param.UpdateTime = param.CreateTime;
  5034. param.CreateSource = createSource;
  5035. let canSave = true;
  5036. if (field1 && (field1.indexOf("]undefined[/图]") > 0 || field1.indexOf("][/图]") > 0 || field2.indexOf("]undefined[/图]") > 0 || field2.indexOf("][/图]") > 0 || field3.indexOf("]undefined[/图]") > 0 || field3.indexOf("][/图]") > 0))
  5037. canSave = false;
  5038. if (canSave) {
  5039. const insertResult = await miaoguo.AddCard(param);
  5040. const obj = {
  5041. MiaoguoCardID: insertResult.insertId,
  5042. ContentType: 0,
  5043. Content: tags,
  5044. };
  5045. await miaoguo.AddCardContent(obj);
  5046. obj.ContentType = 1;
  5047. obj.Content = updateSoundError(field1, createSource);
  5048. await miaoguo.AddCardContent(obj);
  5049. obj.ContentType = 2;
  5050. obj.Content = updateSoundError(field2, createSource);
  5051. await miaoguo.AddCardContent(obj);
  5052. obj.ContentType = 3;
  5053. obj.Content = updateSoundError(field3, createSource);
  5054. await miaoguo.AddCardContent(obj);
  5055. miaoguo.AddMiaoguoLog({
  5056. UserID: ctx.query.UserID,
  5057. LogID: 297,
  5058. Content: JSON.stringify(param),
  5059. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  5060. }
  5061. }
  5062. globalCache.delete("GetMiaoguoCard###UserID=" + param.UserID);
  5063. ctx.body = { errcode: 10000 };
  5064. function updateSoundError(content, source) {
  5065. if (content) {
  5066. const arr = constantClass.GetSoundErrorArr();
  5067. let str = content;
  5068. while (str.indexOf("[读 src=") >= 0) {
  5069. str = str.substr(str.indexOf("[读 src=") + 8);
  5070. const sound = str.substr(0, str.indexOf("'"));
  5071. for (const item of arr) {
  5072. if (sound.indexOf(item.Word) >= 0 && sound.indexOf(item.Sound) < 0) {
  5073. const sound2 = sound.replace(item.Word, item.Sound);
  5074. content = content.replace("[读 src='" + sound, "[读 src=''" + sound2);
  5075. break;
  5076. }
  5077. }
  5078. }
  5079. }
  5080. return content;
  5081. }
  5082. }
  5083. export async function BuildMiaoguoTestCard(ctx) {
  5084. const param = ctx.request.body || {};
  5085. param.UserID = ctx.query.UserID || 0;
  5086. if (param.FolderID == -1) {
  5087. const arrColor = constantClass.getFolderColor;
  5088. const arrFolderIcon = constantClass.getFolderIcon;
  5089. const numColor = stringUtils.Random(0, arrColor.length - 1);
  5090. const numIcon = stringUtils.Random(0, arrFolderIcon.length - 1);
  5091. const obj2 = {
  5092. UserID: param.UserID || 0,
  5093. Name: param.UnitName + "_" + moment().format("MMDDHHmm"),
  5094. FolderType: 0,
  5095. ColorName: arrColor[numColor].Color,
  5096. IconName: arrFolderIcon[numIcon],
  5097. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  5098. Flag: 0,
  5099. };
  5100. const insertID = await miaoguo.AddFolderInfo(obj2);
  5101. param.FolderID = insertID.insertId;
  5102. } else if (param.FolderID > 0) {
  5103. await miaoguo.UpdateFolderInfo({
  5104. ID: param.FolderID,
  5105. UserID: param.UserID,
  5106. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  5107. });
  5108. }
  5109. const list = await fetchLocalResult("/api/GetMiaoguoTestExamine?BookID=" + param.BookID + "&UnitName=" + param.UnitName + "&UnitID=" + param.UnitID + "&TestType=" + param.TestType + "&IsBuild=1");
  5110. if (param.WordList.length > 0) {
  5111. for (let i = 0; i < param.WordList.length; i++) {
  5112. if (param.WordList[i] && param.WordList[i].Word)
  5113. param.WordList[i] = param.WordList[i].Word;
  5114. }
  5115. }
  5116. const arr = [];
  5117. for (const word of param.WordList) {
  5118. for (const item of list) {
  5119. if (word == item.Word) {
  5120. const obj = {
  5121. Field1: item.Content[1].Content,
  5122. Field2: item.Content[2].Content,
  5123. Field3: item.Content[3] && item.Content[3].Content ? item.Content[3].Content : "",
  5124. Tags: item.Content[0].Content,
  5125. ID: 0,
  5126. FolderID: param.FolderID,
  5127. LimitTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  5128. FontSize: item.FontSize,
  5129. };
  5130. if (!stringUtils.CheckIsArray(obj.Tags))
  5131. obj.Tags = obj.Tags.split(",");
  5132. arr.push(obj);
  5133. break;
  5134. }
  5135. }
  5136. }
  5137. for (const postJSON of arr)
  5138. await fetchLocalPost("/api/AddMiaoguoCard?Type=IsExist&UserID=" + param.UserID, postJSON);
  5139. ctx.body = { errcode: 10000 };
  5140. }
  5141. export async function BuildMiaoguoCardByYJBDC(ctx) {
  5142. const param = ctx.request.body || {};
  5143. param.UserID = ctx.query.UserID || 0;
  5144. let folderID = 0;
  5145. const folderInfo = await miaoguo.GetMiaoguoFolderCount({ UserID: param.UserID, FolderType: 2 });
  5146. if (!folderInfo || folderInfo.length == 0) {
  5147. const insertID = await miaoguo.AddFolderInfo({
  5148. UserID: ctx.query.UserID || 0,
  5149. Name: "语境背单词",
  5150. FolderType: 2,
  5151. ColorName: "#7C63DC",
  5152. IconName: "cardlist_paper_white_48x62",
  5153. UpdateTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  5154. Flag: 0,
  5155. });
  5156. folderID = insertID.insertId;
  5157. } else if (folderInfo.length > 0) {
  5158. folderID = folderInfo[0].ID;
  5159. }
  5160. const arrWords = param.Words.split(",");
  5161. for (let i = 0; i < arrWords.length; i++)
  5162. arrWords[i] = "'" + arrWords[i].replace(/'/g, "''") + "'";
  5163. const list = await miaoguo.RunSql(null, "select * from Words where BookID=110 and Word in (" + arrWords.join(",") + ");");
  5164. const arr = [];
  5165. for (const item of list) {
  5166. const question = "[特]" + item.Word + "[/特]";
  5167. let answer = "";
  5168. if (item.Soundmark)
  5169. answer = "[读 src='https://sp0.baidu.com/-rM1hT4a2gU2pMbgoY3K/gettts?lan=uk&text=" + item.Word + "&spd=3&source=alading']" + item.Soundmark + "[/读]\n \n";
  5170. answer += "[光]" + item.Translate + "[/光]\n \n";
  5171. if (item.ExampleSentence) {
  5172. answer += "例句:\n";
  5173. const json = JSON.parse(item.ExampleSentence);
  5174. const sentences = json.Sentences;
  5175. const count = sentences.length > 3 ? 3 : sentences.length;
  5176. for (let j = 0; j < count; j++) {
  5177. answer += "(" + (j + 1) + ")" + sentences[j].Sentence + "\n";
  5178. answer += sentences[j].Translate + "\n\n";
  5179. }
  5180. }
  5181. arr.push({
  5182. Field1: question,
  5183. Field2: answer,
  5184. Field3: "",
  5185. Tags: ["语境", "单词"],
  5186. ID: 0,
  5187. FolderID: folderID,
  5188. LimitTime: moment().format("YYYY-MM-DD HH:mm:ss"),
  5189. });
  5190. }
  5191. for (const postJSON of arr)
  5192. await fetchLocalPost("/api/AddMiaoguoCard?Type=IsExist&UserID=" + param.UserID, postJSON);
  5193. ctx.body = { errcode: 10000 };
  5194. }
  5195. export async function UpdateMiaoguoCardTodayAll(ctx) {
  5196. const paramBody = ctx.request.body || {};
  5197. let currentTime = moment().format('YYYY-MM-DD HH:mm:ss');
  5198. const logInfo = await miaoguo.GetMiaoguoLogInfo({ UserID: ctx.query.UserID, LogID: 304 });
  5199. if (logInfo && logInfo.length > 0) {
  5200. const today = moment().format('YYYY-MM-DD');
  5201. const startDate = moment(logInfo[0].CreateTime).format('YYYY-MM-DD');
  5202. if (startDate < today)
  5203. currentTime = startDate + " 23:59:59";
  5204. }
  5205. for (const item of paramBody.List || []) {
  5206. const param = {
  5207. MiaoguoCardID: item.MiaoguoCardID,
  5208. UserID: ctx.query.UserID,
  5209. IntervalTime: item.IntervalTime || "1d",
  5210. BtnNumber: item.BtnNumber || 4,
  5211. FontSize: item.FontSize || 48,
  5212. Duration: item.Duration || 0,
  5213. LearningType: item.LearningType || 0,
  5214. IsUndo: item.IsUndo || 0,
  5215. LastTime: item.LastTime || currentTime,
  5216. };
  5217. if (param.LastTime > currentTime)
  5218. param.LastTime = currentTime;
  5219. if (param.Duration == "undefined")
  5220. param.Duration = 0;
  5221. let info = await miaoguo.GetCardInfo(param);
  5222. if (info && info.length > 0) {
  5223. info = info[0];
  5224. if (param.BtnNumber != 1 && param.BtnNumber != -1) {
  5225. info.LastTime = param.LastTime;
  5226. info.FirstTime = moment(info.FirstTime).format('YYYY-MM-DD HH:mm:ss');
  5227. if (info.FirstTime < "1900-01-02 00:00:00")
  5228. info.FirstTime = info.LastTime;
  5229. info.LearnNumber = info.LearnNumber + 1;
  5230. let btnNumber = param.BtnNumber;
  5231. if (param.LearningType == 0 && btnNumber < 4)
  5232. btnNumber = 4;
  5233. if (info.RepetitionCoefficient > 1.3)
  5234. info.RepetitionCoefficient = getEF(info.RepetitionCoefficient, btnNumber);
  5235. else
  5236. info.RepetitionCoefficient = 1.5;
  5237. }
  5238. if (param.BtnNumber == 1) {
  5239. info.LastTime = param.LastTime;
  5240. const days = Number(param.IntervalTime.replace("d", ""));
  5241. info.LimitTime = moment().add(days, "days").format('YYYY-MM-DD HH:mm:ss');
  5242. } else if (param.IntervalTime) {
  5243. const intervalTime = param.IntervalTime;
  5244. if (intervalTime.indexOf("m") > 0) {
  5245. const minute = Number(intervalTime.replace("m", ""));
  5246. info.LimitTime = moment().add(minute, "minute").format('YYYY-MM-DD HH:mm:ss');
  5247. info.IntervalDay = 0;
  5248. } else if (intervalTime.indexOf("d") > 0) {
  5249. const days = Number(intervalTime.replace("d", ""));
  5250. info.LimitTime = moment().add(days, "days").format('YYYY-MM-DD HH:mm:ss');
  5251. info.IntervalDay = days;
  5252. }
  5253. }
  5254. if (param.FontSize)
  5255. info.FontSize = param.FontSize;
  5256. if (param.BtnNumber == -1) {
  5257. info.IsTodayStudy = 1;
  5258. info.LimitTime = moment().format('YYYY-MM-DD HH:mm:ss');
  5259. info.IntervalDay = 0;
  5260. } else if (info.IsTodayStudy == 1 && param.BtnNumber > 0) {
  5261. info.IsTodayStudy = 0;
  5262. }
  5263. await miaoguo.UpdateCard(info);
  5264. const obj = {
  5265. CardID: param.MiaoguoCardID,
  5266. CardType: info.CardType,
  5267. CreateTime: param.LastTime,
  5268. LearningType: param.LearningType,
  5269. BtnNumber: Number(param.BtnNumber),
  5270. RepetitionCoefficient: info.RepetitionCoefficient,
  5271. };
  5272. if (param.IntervalTime) {
  5273. if (param.BtnNumber == 1)
  5274. obj.IntervalTime = "1d";
  5275. else
  5276. obj.IntervalTime = param.IntervalTime;
  5277. }
  5278. if (param.Duration) {
  5279. obj.Duration = param.Duration;
  5280. if (param.Duration > 1800)
  5281. obj.Duration = 1800;
  5282. }
  5283. const learningRecord = await miaoguo.GetLearningRecordInfoNew(obj);
  5284. if (!learningRecord || learningRecord.length == 0)
  5285. await miaoguo.AddLearningRecord(obj);
  5286. }
  5287. }
  5288. ctx.body = { errcode: 10000, result: 1 };
  5289. miaoguo.AddMiaoguoLog({
  5290. UserID: ctx.query.UserID,
  5291. LogID: 312,
  5292. Content: JSON.stringify(paramBody),
  5293. }).catch(err => console.error('AddMiaoguoLog failed:', err));
  5294. globalCache.delete("GetMiaoguoCard###UserID=" + ctx.query.UserID);
  5295. }
  5296. export async function UpdateMiaoguoCardInfo(ctx) {
  5297. const param = {
  5298. UserID: ctx.query.UserID || "0",
  5299. MiaoguoCardID: ctx.query.MiaoguoCardID || "0",
  5300. IsCollect: ctx.query.IsCollect,
  5301. };
  5302. const info = await miaoguo.GetCardInfo(param);
  5303. if (info && info.length > 0) {
  5304. const obj = {
  5305. MiaoguoCardID: param.MiaoguoCardID,
  5306. };
  5307. if (param.IsCollect)
  5308. obj.IsCollect = param.IsCollect;
  5309. await miaoguo.UpdateCard(obj);
  5310. const log = {
  5311. UserID: param.UserID,
  5312. LogID: 298,
  5313. Content: JSON.stringify(obj),
  5314. };
  5315. miaoguo.AddMiaoguoLog(log).catch(err => console.error('AddMiaoguoLog failed:', err));
  5316. }
  5317. ctx.body = { errcode: 10000 };
  5318. }
  5319. export async function UpdateCardImageUrl(ctx) {
  5320. const param = {
  5321. MiaoguoCardID: ctx.query.MiaoguoCardID,
  5322. };
  5323. const list = await miaoguo.GetCardContentList(param.MiaoguoCardID);
  5324. if (list && list.length > 0) {
  5325. for (let i = 1; i < list.length; i++) {
  5326. let temp = list[i].Content;
  5327. if (!temp) continue;
  5328. let changed = false;
  5329. const startTime = Date.now();
  5330. while (temp.indexOf("[图") >= 0) {
  5331. temp = temp.substr(temp.indexOf("[图 url='"));
  5332. const oldUrl = temp.substring(temp.indexOf("[图 url='") + 8, temp.indexOf("']"));
  5333. const newUrl = temp.substring(temp.indexOf("']") + 2, temp.indexOf("[/图]"));
  5334. if (newUrl) {
  5335. changed = true;
  5336. list[i].Content = list[i].Content.replace(newUrl, "");
  5337. list[i].Content = list[i].Content.replace(oldUrl, newUrl);
  5338. }
  5339. temp = temp.substr(temp.indexOf("[/图]") + 4);
  5340. try {
  5341. stringUtils.loopBreaker(startTime);
  5342. } catch {
  5343. break;
  5344. }
  5345. }
  5346. if (changed) {
  5347. await miaoguo.UpdateCardContent({
  5348. MiaoguoCardID: param.MiaoguoCardID,
  5349. ContentType: list[i].ContentType,
  5350. Content: list[i].Content,
  5351. });
  5352. }
  5353. }
  5354. }
  5355. ctx.body = { errcode: 10000 };
  5356. }
  5357. export async function GetBiShunByKaitiUrl(ctx) {
  5358. const param = ctx.request.body || {};
  5359. let result = param.Url;
  5360. const arr = globalCache.get("HanziWordsAll");
  5361. if (arr !== 0) {
  5362. for (const item of arr) {
  5363. if (param.Url === item.KaitiUrl && param.DataType == "bishun") {
  5364. result = item.BiShunUrl;
  5365. break;
  5366. } else if (param.Url === item.BiShunUrl && param.DataType == "kaiti") {
  5367. result = item.KaitiUrl;
  5368. break;
  5369. }
  5370. }
  5371. }
  5372. ctx.body = { errcode: 10000, result };
  5373. }
  5374. async function fetchLocalResult(path) {
  5375. const response = await fetch('http://localhost:' + config.port + encodeURI(path));
  5376. const json = await response.json();
  5377. return json.result;
  5378. }
  5379. async function fetchLocalPost(path, body) {
  5380. const response = await fetch('http://localhost:' + config.port + encodeURI(path), {
  5381. method: 'POST',
  5382. headers: { 'content-type': 'application/json' },
  5383. body: JSON.stringify(body),
  5384. });
  5385. return await response.json();
  5386. }
  5387. function changeStringToView(field) {
  5388. const result = {
  5389. Field: [[]],
  5390. Images: [],
  5391. };
  5392. for (let j = 0; j < field.length; j++) {
  5393. if (j == 0) {
  5394. if (field[j].ContentType == 0 && field[j].Content && field[j].Content.length > 0)
  5395. result.Tags = field[j].Content.toString().split(",");
  5396. else
  5397. result.Tags = [];
  5398. } else {
  5399. if (field[j].ContentType == j && field[j].Content && field[j].Content.length > 0) {
  5400. const arrResult = [];
  5401. let str = field[j].Content.toString();
  5402. str = decryptUrl(str);
  5403. if (str.indexOf("[读") > 0)
  5404. str = str.replace(/\[读/g, "\n[读");
  5405. str = str.replace(/\[图/g, "\n[图");
  5406. if (str.indexOf("[音") > 0)
  5407. str = str.replace(/\[音/g, "\n[音");
  5408. str = str.replace(/\[\/读\]/g, "[/读]\n");
  5409. str = str.replace(/\[\/图\]/g, "[/图]\n");
  5410. str = str.replace(/\[\/音\]/g, "[/音]\n");
  5411. str = str.replace(/\n\n\n/g, "\n\n");
  5412. const arr = str.split("\n");
  5413. for (let k = 0; k < arr.length; k++) {
  5414. if (arr[k].indexOf("[图") >= 0 && arr[k].indexOf("[/图]") > 0) {
  5415. const obj = { Type: "image" };
  5416. obj.ContentServer = arr[k].substring(arr[k].indexOf("[图") + 3, arr[k].indexOf("[/图]"));
  5417. const w = 650;
  5418. if (obj.ContentServer.indexOf("w='") >= 0 && obj.ContentServer.indexOf("h='") >= 0) {
  5419. obj.Width = obj.ContentServer.substring(obj.ContentServer.indexOf("w='") + 3, obj.ContentServer.indexOf("h='") - 2);
  5420. obj.Height = obj.ContentServer.substring(obj.ContentServer.indexOf("h='") + 3, obj.ContentServer.indexOf("']"));
  5421. obj.Height = Math.round((w * Number(obj.Height)) / Number(obj.Width));
  5422. obj.Width = w;
  5423. } else {
  5424. obj.Width = "";
  5425. obj.Height = "";
  5426. }
  5427. obj.ContentServer = obj.ContentServer.substring(obj.ContentServer.indexOf("]") + 1);
  5428. obj.Content = obj.ContentServer;
  5429. if (obj.Content.lastIndexOf("fm=58") >= 0)
  5430. obj.IsShowBishun = true;
  5431. else if (obj.Content.indexOf("hanyu-word-gif") > 0)
  5432. obj.IsShowKaiti = true;
  5433. result.Images.push(obj.Content);
  5434. arrResult.push(obj);
  5435. } else if (arr[k].indexOf("[特]") >= 0 && arr[k].indexOf("[/特]") > 0) {
  5436. arrResult.push({
  5437. Type: "bigword",
  5438. Content: arr[k].substring(arr[k].indexOf("[特]") + 3, arr[k].indexOf("[/特]")),
  5439. });
  5440. } else if ((arr[k].indexOf("[线]") >= 0 && arr[k].indexOf("[/线]") > 0) || (arr[k].indexOf("[光]") >= 0 && arr[k].indexOf("[/光]") > 0)) {
  5441. const content = [];
  5442. let temp = arr[k];
  5443. do {
  5444. const num1 = temp.indexOf("[线]");
  5445. const num2 = temp.indexOf("[光]");
  5446. if (num1 >= 0 || num2 >= 0) {
  5447. if ((num1 < num2 && num1 >= 0 && num2 >= 0) || (num1 >= 0 && num2 < 0)) {
  5448. const temp1 = temp.substring(0, temp.indexOf("[线]"));
  5449. if (temp1)
  5450. content.push({ key: "normal", value: temp1 });
  5451. else if (temp && temp.indexOf("[线]") < 0) {
  5452. content.push({ key: "normal", value: temp });
  5453. temp = "";
  5454. }
  5455. } else if ((num1 > num2 && num1 >= 0 && num2 >= 0) || (num1 < 0 && num2 >= 0)) {
  5456. const temp1 = temp.substring(0, temp.indexOf("[光]"));
  5457. if (temp1)
  5458. content.push({ key: "normal", value: temp1 });
  5459. else if (temp && temp.indexOf("[光]") < 0) {
  5460. content.push({ key: "normal", value: temp });
  5461. temp = "";
  5462. }
  5463. } else {
  5464. content.push({ key: "normal", value: temp });
  5465. temp = "";
  5466. }
  5467. } else {
  5468. content.push({ key: "normal", value: temp });
  5469. temp = "";
  5470. }
  5471. if (temp.length > 0 && (num1 >= 0 || num2 >= 0)) {
  5472. if ((num1 < num2 && num1 >= 0 && num2 >= 0) || (num1 >= 0 && num2 < 0)) {
  5473. temp = temp.substr(temp.indexOf("[线]") + 3);
  5474. const temp1 = temp.substring(0, temp.indexOf("[/线]"));
  5475. if (temp1)
  5476. content.push({ key: "line", value: temp1 });
  5477. temp = temp.substr(temp.indexOf("[/线]") + 4);
  5478. } else if ((num1 > num2 && num1 >= 0 && num2 >= 0) || (num1 < 0 && num2 >= 0)) {
  5479. temp = temp.substr(temp.indexOf("[光]") + 3);
  5480. const temp1 = temp.substring(0, temp.indexOf("[/光]"));
  5481. if (temp1)
  5482. content.push({ key: "highlighter", value: temp1 });
  5483. temp = temp.substr(temp.indexOf("[/光]") + 4);
  5484. } else {
  5485. temp = "";
  5486. }
  5487. } else {
  5488. temp = "";
  5489. }
  5490. } while (temp.length > 0);
  5491. arrResult.push({ Type: "line", Content: content });
  5492. } else if (arr[k].indexOf("[读") >= 0 && arr[k].indexOf("[/读]") > 0) {
  5493. const obj = { Type: "sound" };
  5494. if (arr[k].indexOf("[读]") >= 0) {
  5495. obj.Content = arr[k].substring(arr[k].indexOf("[读]") + 3, arr[k].indexOf("[/读]"));
  5496. } else {
  5497. const tempIndex = arr[k].indexOf("']") + 2;
  5498. obj.Content = arr[k].substring(tempIndex, arr[k].indexOf("[/读]"));
  5499. obj.SoundMark = arr[k].substring(arr[k].indexOf("src='") + 5, arr[k].indexOf("']"));
  5500. }
  5501. arrResult.push(obj);
  5502. } else if (arr[k].indexOf("[音") >= 0 && arr[k].indexOf("[/音]") > 0) {
  5503. const obj = { Type: "recorder" };
  5504. obj.SoundMark = arr[k].substring(arr[k].indexOf("url='") + 5, arr[k].indexOf("']"));
  5505. arrResult.push(obj);
  5506. } else if (arr[k] != "") {
  5507. arrResult.push({ Type: "normal", Content: arr[k] });
  5508. } else if (k > 0 && arr[k] == "") {
  5509. arrResult.push({ Type: "br", Content: "" });
  5510. }
  5511. arrResult.push({ Type: "return" });
  5512. }
  5513. for (let i = arrResult.length - 1; i >= 0; i--) {
  5514. if (arrResult[i].Type == "return" || arrResult[i].Type == "br")
  5515. arrResult.pop();
  5516. else
  5517. break;
  5518. }
  5519. result.Field.push(arrResult);
  5520. } else {
  5521. result.Field.push([]);
  5522. }
  5523. }
  5524. }
  5525. return result;
  5526. }
  5527. function decryptUrl(str) {
  5528. if (str && str.constructor == Array) {
  5529. if (str.length > 0)
  5530. str = str.join(",");
  5531. else
  5532. str = "";
  5533. }
  5534. if (str) {
  5535. str = str.replace(/#####1#####/g, "baidu.com");
  5536. str = str.replace(/#####2#####/g, "iciba.com");
  5537. str = str.replace(/#####3#####/g, "https://pinyin.kylx365.com/sounds");
  5538. str = str.replace(/#####4#####/g, "https://miaguo-1253256735.file.myqcloud.com");
  5539. }
  5540. return str;
  5541. }
  5542. function getPinyinNormal(pinyin) {
  5543. let result = "";
  5544. const arr = constantClass.getPinyinArray();
  5545. for (const item of arr) {
  5546. if (item[1] == pinyin) {
  5547. result = item[0];
  5548. break;
  5549. }
  5550. }
  5551. return result;
  5552. }
  5553. function getEF(EF, Q) {
  5554. if (Q < 3)
  5555. Q = 3;
  5556. let result = Math.round(1000 * (EF + (0.1 - (5 - Q) * (0.08 + (5 - Q) * 0.02)))) / 1000;
  5557. if (result < 1.6)
  5558. result = 1.6;
  5559. if (EF < 1.8 && Q == 4)
  5560. result = 1.8;
  5561. if (EF < 2 && Q == 5)
  5562. result = 2;
  5563. return result;
  5564. }
  5565. function getWordJSON(param, words) {
  5566. const taskList = [];
  5567. if (words[0].Pinyin) {
  5568. for (const word of words) {
  5569. const taskInfo = {
  5570. FontSize: 144,
  5571. TagWidth: 136,
  5572. Content: [
  5573. { ContentType: 0, Content: [] },
  5574. { ContentType: 1, Content: [] },
  5575. { ContentType: 2, Content: [] },
  5576. ],
  5577. };
  5578. taskInfo.Word = word.Name;
  5579. const pinyinNormal = "(" + getPinyinNormal(word.Pinyin) + ")";
  5580. const combineWords = stringUtils.ReplaceAllString(word.CombineWords, word.Name, word.Name + pinyinNormal);
  5581. taskInfo.ReadString = word.Name + pinyinNormal + "," + combineWords + "的" + word.Name + pinyinNormal;
  5582. if (param.TestType == "read") {
  5583. taskInfo.Content[0].Content = ["怎么念"];
  5584. let question = word.CombineWords;
  5585. question = stringUtils.ReplaceAllString(question, word.Name, "(" + word.Name + ")");
  5586. taskInfo.Content[1].Content = question;
  5587. taskInfo.Content[2].Content = "[读 src='" + taskInfo.ReadString + "']" + word.Pinyin + "[/读]";
  5588. if (word.CombineWords.length > 6) {
  5589. taskInfo.FontSize = 108;
  5590. if (word.CombineWords.length > 10)
  5591. taskInfo.FontSize = 68;
  5592. }
  5593. } else if (param.TestType == "write") {
  5594. taskInfo.Content[0].Content = ["怎么写"];
  5595. const question2 = word.CombineWords2;
  5596. taskInfo.Content[1].Content = "[读 src='" + taskInfo.ReadString + "']" + question2 + "[/读]";
  5597. taskInfo.Content[2].Content = word.Name + "[图 w='650' h='650']" + word.KaitiUrl + "[/图]";
  5598. if (question2.length > 6) {
  5599. taskInfo.FontSize = 108;
  5600. if (question2.length > 10) {
  5601. taskInfo.FontSize = 68;
  5602. if (question2.length > 14)
  5603. taskInfo.FontSize = 48;
  5604. }
  5605. }
  5606. }
  5607. taskInfo.ContentNew = changeStringToView(taskInfo.Content);
  5608. if (!param.IsBuild)
  5609. delete taskInfo.Content;
  5610. taskList.push(taskInfo);
  5611. }
  5612. }
  5613. return taskList;
  5614. }