miaoguoController.js 256 KB

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