PersonnelModuleController.cs 196 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675
  1. using Aspose.Cells;
  2. using FluentValidation;
  3. using Markdig;
  4. using Microsoft.AspNetCore.SignalR;
  5. using OASystem.API.OAMethodLib;
  6. using OASystem.API.OAMethodLib.DeepSeekAPI;
  7. using OASystem.API.OAMethodLib.Hub.HubClients;
  8. using OASystem.API.OAMethodLib.Hub.Hubs;
  9. using OASystem.API.OAMethodLib.QiYeWeChatAPI;
  10. using OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice;
  11. using OASystem.Domain.Dtos.Groups;
  12. using OASystem.Domain.Dtos.PersonnelModule;
  13. using OASystem.Domain.Dtos.Statistics;
  14. using OASystem.Domain.Entities;
  15. using OASystem.Domain.Entities.Groups;
  16. using OASystem.Domain.Entities.PersonnelModule;
  17. using OASystem.Domain.ViewModels.PersonnelModule;
  18. using OASystem.Domain.ViewModels.QiYeWeChat;
  19. using OASystem.Domain.ViewModels.Statistics;
  20. using OASystem.Infrastructure.Repositories.Groups;
  21. using OASystem.Infrastructure.Repositories.PersonnelModule;
  22. using System.Data;
  23. using System.Diagnostics;
  24. using System.Globalization;
  25. using static OASystem.API.OAMethodLib.JWTHelper;
  26. namespace OASystem.API.Controllers
  27. {
  28. /// <summary>
  29. /// 人事模块
  30. /// </summary>
  31. [Route("api/[controller]/[action]")]
  32. public class PersonnelModuleController : ControllerBase
  33. {
  34. private Result _result;
  35. private readonly IMapper _mapper;
  36. //private readonly decimal _chengDuMinimumWage = 2100.00M;
  37. private readonly IQiYeWeChatApiService _qiYeWeChatApiService;
  38. private readonly WageSheetRepository _wageSheetRep;
  39. private readonly UsersRepository _usersRep;
  40. private readonly TaskAllocationRepository _taskAllocationRep;
  41. private readonly SqlSugarClient _sqlSugar;
  42. private readonly IHubContext<ChatHub, IChatClient> _hubContext;
  43. private readonly GoodsRepository _goodsRep;
  44. private readonly DecreasePaymentsRepository _otherPaymentRep;
  45. private readonly FeeAuditRepository _feeAuditRep;
  46. private readonly IDeepSeekService _deepSeekService;
  47. private readonly ILogger<PersonnelModuleController> _logger;
  48. private readonly string url;
  49. private readonly string path;
  50. /// <summary>
  51. /// 初始化
  52. /// </summary>
  53. /// <param name="qiYeWeChatApiService"></param>
  54. /// <param name="wageSheetRep"></param>
  55. /// <param name="usersRep"></param>
  56. /// <param name="mapper"></param>
  57. /// <param name="taskAllocationRep"></param>
  58. /// <param name="hubContext"></param>
  59. /// <param name="goodsRep"></param>
  60. /// <param name="sqlSugar"></param>
  61. /// <param name="otherPaymentRep"></param>
  62. /// <param name="feeAuditRep"></param>
  63. /// <param name="deepSeekService"></param>
  64. /// <param name="logger"></param>
  65. public PersonnelModuleController(
  66. IHubContext<ChatHub, IChatClient> hubContext,
  67. IMapper mapper,
  68. IQiYeWeChatApiService qiYeWeChatApiService,
  69. WageSheetRepository wageSheetRep,
  70. UsersRepository usersRep,
  71. TaskAllocationRepository taskAllocationRep,
  72. GoodsRepository goodsRep,
  73. SqlSugarClient sqlSugar,
  74. DecreasePaymentsRepository otherPaymentRep,
  75. FeeAuditRepository feeAuditRep,
  76. IDeepSeekService deepSeekService,
  77. ILogger<PersonnelModuleController> logger
  78. )
  79. {
  80. _mapper = mapper;
  81. _usersRep = usersRep;
  82. _qiYeWeChatApiService = qiYeWeChatApiService;
  83. _wageSheetRep = wageSheetRep;
  84. _result = new Result();
  85. _sqlSugar = sqlSugar;
  86. url = AppSettingsHelper.Get("ExcelBaseUrl");
  87. path = AppSettingsHelper.Get("ExcelBasePath");
  88. if (!System.IO.Directory.Exists(path))
  89. {
  90. System.IO.Directory.CreateDirectory(path);//不存在就创建文件夹
  91. }
  92. this._taskAllocationRep = taskAllocationRep;
  93. _hubContext = hubContext;
  94. _goodsRep = goodsRep;
  95. _otherPaymentRep = otherPaymentRep;
  96. _feeAuditRep = feeAuditRep;
  97. _deepSeekService = deepSeekService;
  98. _logger = logger;
  99. }
  100. #region 工资表单
  101. /// <summary>
  102. /// 工资 月列表
  103. /// </summary>
  104. /// <returns></returns>
  105. [HttpPost]
  106. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  107. public async Task<IActionResult> GetWageSheetMonth()
  108. {
  109. string sql = string.Format("Select * From Pm_WageIssueWorkingDay Where IsDel = 0 Order By YearMonth Desc");
  110. var data = await _wageSheetRep._sqlSugar.SqlQueryable<WageSheetMonthView>(sql).ToListAsync();
  111. return Ok(JsonView(true, "查询成功!", data));
  112. }
  113. /// <summary>
  114. /// 工资 工作日信息
  115. /// 查询
  116. /// </summary>
  117. /// <returns></returns>
  118. [HttpPost]
  119. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  120. public async Task<IActionResult> GetWageSheetMonthWorkdays(string startDt, string endDt)
  121. {
  122. //参数处理
  123. var dtFormat = "yyyy-MM-dd";
  124. var startDtIsValid = DateTime.TryParseExact(startDt, dtFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out _);
  125. var endDtIsValid = DateTime.TryParseExact(endDt, dtFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out _);
  126. if (!startDtIsValid) return Ok(JsonView(false, "开始日期格式错误!正确时间格式:yyyy-MM-dd "));
  127. if (!endDtIsValid) return Ok(JsonView(false, "结束格式错误!正确时间格式:yyyy-MM-dd "));
  128. string sql = string.Format(@"Select * From Sys_Calendar Where Isdel = 0 And Dt between '{0}' And '{1}'", startDt, endDt);
  129. var data = await _sqlSugar.SqlQueryable<CalendarInfoView>(sql).ToListAsync();
  130. return Ok(JsonView(true, "查询成功!", data));
  131. }
  132. /// <summary>
  133. /// 工资 工作日信息
  134. /// 编辑
  135. /// </summary>
  136. /// <returns></returns>
  137. [HttpPost]
  138. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  139. public async Task<IActionResult> GetWageSheetMonthWorkdaysAddOrEdit(WageSheetMonthWorkdaysAddOrEditDto dto)
  140. {
  141. //参数处理
  142. string yearFormat = "yyyy-MM";
  143. string dtFormat = "yyyy-MM-dd";
  144. bool yearDtIsValid = DateTime.TryParseExact(dto.YearMonth, yearFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime yearDt);
  145. bool startDtIsValid = DateTime.TryParseExact(dto.StartDate, dtFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime startDt1);
  146. bool endDtIsValid = DateTime.TryParseExact(dto.EndDate, dtFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime endDt1);
  147. if (!yearDtIsValid) return Ok(JsonView(false, "年月日期格式错误!正确时间格式:yyyy-MM "));
  148. if (!startDtIsValid) return Ok(JsonView(false, "开始日期格式错误!正确时间格式:yyyy-MM-dd "));
  149. if (!endDtIsValid) return Ok(JsonView(false, "结束格式错误!正确时间格式:yyyy-MM-dd "));
  150. #region 处理数据
  151. Pm_WageIssueWorkingDay pm_WageIssueWorkingDay1 = new();
  152. pm_WageIssueWorkingDay1 = _mapper.Map<Pm_WageIssueWorkingDay>(dto);
  153. List<Sys_Calendar> sys_Calendars = new();
  154. sys_Calendars = _mapper.Map<List<Sys_Calendar>>(dto.CalendarInfos);
  155. if (sys_Calendars.Count > 0)
  156. {
  157. sys_Calendars = sys_Calendars.OrderBy(it => it.Dt).ToList();
  158. }
  159. pm_WageIssueWorkingDay1.Workdays = sys_Calendars.Where(it => it.IsWorkDay == true).ToList().Count;
  160. foreach (var item in sys_Calendars)
  161. {
  162. item.Remark = pm_WageIssueWorkingDay1.Remark;
  163. item.CreateUserId = pm_WageIssueWorkingDay1.CreateUserId;
  164. item.CreateTime = pm_WageIssueWorkingDay1.CreateTime;
  165. }
  166. #endregion
  167. var _sqlSugar = _wageSheetRep._sqlSugar;
  168. _sqlSugar.BeginTran();
  169. try
  170. {
  171. List<Sys_Calendar> sys_Calendars_add = new();
  172. List<Sys_Calendar> sys_Calendars_update = new();
  173. sys_Calendars_add = sys_Calendars.Where(it => it.Id == 0).OrderBy(it => it.Dt).ToList();
  174. sys_Calendars_update = sys_Calendars.Where(it => it.Id != 0).OrderBy(it => it.Dt).ToList();
  175. //int add1 = 0;
  176. //int upd = 0;
  177. if (sys_Calendars_add.Count > 0)
  178. {
  179. var calendarsAdd = await _sqlSugar.Insertable(sys_Calendars_add).ExecuteReturnIdentityAsync();
  180. if (calendarsAdd < 0)
  181. {
  182. _sqlSugar.RollbackTran();
  183. return Ok(JsonView(false, "工作日/节假日/休息日添加失败!"));
  184. }
  185. }
  186. if (sys_Calendars_update.Count > 0)
  187. {
  188. var calendarsUpdate = await _sqlSugar.Updateable<Sys_Calendar>(sys_Calendars)
  189. .UpdateColumns(it => new { it.Dt, it.IsWorkDay, it.IsHoliDay, it.HoliName })
  190. .WhereColumns(it => it.Id)
  191. .ExecuteCommandAsync();
  192. if (calendarsUpdate < 0)
  193. {
  194. _sqlSugar.RollbackTran();
  195. return Ok(JsonView(false, "工作日/节假日/休息日编辑失败!"));
  196. }
  197. }
  198. var calendarsDatas = await _sqlSugar.Queryable<Sys_Calendar>()
  199. .Where(it => Convert.ToDateTime(it.Dt) >= Convert.ToDateTime(dto.StartDate) && Convert.ToDateTime(it.Dt) <= Convert.ToDateTime(dto.EndDate)).ToListAsync();
  200. if (calendarsDatas.Count < 0)
  201. {
  202. _sqlSugar.RollbackTran();
  203. return Ok(JsonView(false, "日期包暂无工作日/节假日/休息日的信息!"));
  204. }
  205. //月份表是否存在
  206. Pm_WageIssueWorkingDay pm_WageIssueWorkingDay = new()
  207. {
  208. YearMonth = dto.YearMonth,
  209. StartDate = dto.StartDate,
  210. EndDate = dto.EndDate,
  211. Workdays = calendarsDatas.Where(it => it.IsWorkDay == true).ToList().Count
  212. };
  213. string sql = string.Format("Select * From Pm_WageIssueWorkingDay Where Isdel = 0 And YearMonth='{0}'", dto.YearMonth);
  214. var workdsys = await _sqlSugar.SqlQueryable<Pm_WageIssueWorkingDay>(sql).FirstAsync();
  215. pm_WageIssueWorkingDay.CreateUserId = dto.UserId;
  216. pm_WageIssueWorkingDay.IsDel = 0;
  217. if (workdsys == null) //添加
  218. {
  219. int addId = await _sqlSugar.Insertable(pm_WageIssueWorkingDay).ExecuteReturnIdentityAsync();
  220. if (addId < 0)
  221. {
  222. _sqlSugar.RollbackTran();
  223. return Ok(JsonView(false, "工作日设置添加失败!"));
  224. }
  225. }
  226. else //更新
  227. {
  228. int updCount = await _sqlSugar.Updateable(pm_WageIssueWorkingDay)
  229. .IgnoreColumns(z => new { z.CreateUserId, z.CreateTime, z.DeleteUserId, z.DeleteTime, z.IsDel })
  230. .WhereColumns(it => it.YearMonth)
  231. .ExecuteCommandAsync();
  232. if (updCount < 0)
  233. {
  234. _sqlSugar.RollbackTran();
  235. return Ok(JsonView(false, "工作日设置编辑失败!"));
  236. }
  237. }
  238. _sqlSugar.CommitTran();
  239. return Ok(JsonView(true, "操作成功!"));
  240. }
  241. catch (Exception ex)
  242. {
  243. _sqlSugar.RollbackTran();
  244. return Ok(JsonView(false, ex.Message));
  245. }
  246. }
  247. /// <summary>
  248. /// 工资表单 基础数据源
  249. /// </summary>
  250. /// <returns></returns>
  251. [HttpPost]
  252. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  253. public async Task<IActionResult> GetWageSheetBasicsDataSource()
  254. {
  255. string companySql = string.Format("Select * From Sys_Company Where IsDel = 0");
  256. var compnayData = await _wageSheetRep._sqlSugar.SqlQueryable<CompanyNameView>(companySql).ToListAsync();
  257. string depSql = string.Format("Select * From Sys_Department Where IsDel = 0");
  258. var depData = await _wageSheetRep._sqlSugar.SqlQueryable<Domain.ViewModels.System.DepartmentView>(depSql).ToListAsync();
  259. //获取OA系统内所有用户
  260. var nameData = await _usersRep.GetUserNameList(1);
  261. if (nameData.Code != 0)
  262. {
  263. return Ok(JsonView(false, nameData.Msg));
  264. }
  265. var data = new
  266. {
  267. compnayData,
  268. depData,
  269. userNames = nameData.Data
  270. };
  271. return Ok(JsonView(true, "查询成功!", data));
  272. }
  273. /// <summary>
  274. /// 获取工资发放月份
  275. /// </summary>
  276. /// <param name="dto"></param>
  277. /// <returns></returns>
  278. [HttpPost]
  279. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  280. public async Task<IActionResult> GetWageYaerMonths(WageYearDto dto)
  281. {
  282. string sql = string.Format(@"Select * From Pm_WageIssueWorkingDay
  283. Where Isdel = 0 And YearMonth Like '%{0}%'
  284. Order By YearMonth Asc", dto.Year);
  285. var data = await _wageSheetRep._sqlSugar.SqlQueryable<WageYearMonthView>(sql).ToListAsync();
  286. return Ok(JsonView(true, "操作成功!", data));
  287. }
  288. /// <summary>
  289. /// 获取工资表单
  290. /// </summary>
  291. /// <param name="dto"></param>
  292. /// <returns></returns>
  293. [HttpPost]
  294. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  295. public async Task<IActionResult> GetWageSheetList(WageSheetListDto dto)
  296. {
  297. if (string.IsNullOrEmpty(dto.YearMonth)) return Ok(JsonView(false, "请选择日期格式"));
  298. //验证日期格式
  299. if (!DateTime.TryParse(dto.YearMonth, out DateTime yearMonthDt))
  300. {
  301. return Ok(JsonView(false, "无效的日期格式"));
  302. }
  303. //参数处理
  304. //string ymFormat = "yyyy-MM";
  305. //bool yearMonthDttIsValid = DateTime.TryParseExact(dto.YearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime yearMonthDt);
  306. //if (!yearMonthDttIsValid)
  307. //{
  308. // _result.Msg = "年月格式错误!正确时间格式:yyyy-MM ";
  309. // return Ok(JsonView(false, _result.Msg));
  310. //}
  311. //获取月工资数据
  312. string yearMonth = yearMonthDt.ToString("yyyy-MM");
  313. if (dto.PortType == 1)
  314. {
  315. _result = await _wageSheetRep.Get_WageSheet_ListByYearMonthAsync(yearMonth);
  316. if (_result.Code != 0)
  317. {
  318. return Ok(JsonView(false, _result.Msg));
  319. }
  320. }
  321. else if (dto.PortType == 2)
  322. { }
  323. else if (dto.PortType == 3)
  324. { }
  325. else
  326. {
  327. return Ok(JsonView(false, "请选择正确的端口参数"));
  328. }
  329. return Ok(JsonView(true, _result.Msg, _result.Data));
  330. }
  331. /// <summary>
  332. /// 获取工资 详情
  333. /// </summary>
  334. /// <param name="dto"></param>
  335. /// <returns></returns>
  336. [HttpPost]
  337. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  338. public async Task<IActionResult> GetWageSheetById(WageSheetInfoDto dto)
  339. {
  340. if (dto.PortType == 1)
  341. {
  342. _result = await _wageSheetRep.Get_WageSheet_InfoByIdAsync(dto.Id);
  343. if (_result.Code != 0)
  344. {
  345. return Ok(JsonView(false, _result.Msg));
  346. }
  347. }
  348. else if (dto.PortType == 2)
  349. { }
  350. else if (dto.PortType == 3)
  351. { }
  352. else
  353. {
  354. return Ok(JsonView(false, "请选择正确的端口参数"));
  355. }
  356. return Ok(JsonView(true, _result.Msg, _result.Data));
  357. }
  358. /// <summary>
  359. /// 人事模块 工资表单 删除
  360. /// </summary>
  361. /// <param name="dto"></param>
  362. /// <returns></returns>
  363. [HttpPost]
  364. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  365. public async Task<IActionResult> PostWageSheetDel(WageDelDto dto)
  366. {
  367. try
  368. {
  369. _result = await _wageSheetRep.Post_WageSheet_DelAsync(dto);
  370. if (_result.Code != 0)
  371. {
  372. return Ok(JsonView(false, _result.Msg));
  373. }
  374. }
  375. catch (Exception ex)
  376. {
  377. return Ok(JsonView(false, ex.Message));
  378. }
  379. return Ok(JsonView(true, _result.Msg, _result.Data));
  380. }
  381. /// <summary>
  382. /// 人事模块 工资表单 添加 Or 修改
  383. /// </summary>
  384. /// <param name="dto"></param>
  385. /// <returns></returns>
  386. [HttpPost]
  387. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  388. public async Task<IActionResult> PostWageSheetAddOrEdit(WageAddOrEditDto dto)
  389. {
  390. try
  391. {
  392. Pm_WageSheet pm_WageSheet = new();
  393. pm_WageSheet = _mapper.Map<Pm_WageSheet>(dto);
  394. pm_WageSheet.LastUpdateUserId = dto.CreateUserId;
  395. #region 计算工资
  396. //月工资
  397. decimal salary = pm_WageSheet.Basic + pm_WageSheet.Floats + pm_WageSheet.PostAllowance + pm_WageSheet.InformationSecurityFee + pm_WageSheet.OtherSubsidies;
  398. //扣款合计
  399. decimal totalDeduction = pm_WageSheet.SickLeave + pm_WageSheet.SomethingFalse + pm_WageSheet.LateTo + pm_WageSheet.LeaveEarly + pm_WageSheet.Absenteeism + pm_WageSheet.NotPunch +
  400. pm_WageSheet.ReservedFunds + pm_WageSheet.WithholdingInsurance + pm_WageSheet.OtherDeductions + pm_WageSheet.OtherDeductions;
  401. //实发合计 不含个税
  402. if (pm_WageSheet.RegularDays >= pm_WageSheet.WorkDays)
  403. {
  404. pm_WageSheet.RegularDays = pm_WageSheet.WorkDays;
  405. salary = salary + pm_WageSheet.Mealsupplement + pm_WageSheet.OtherHandle;
  406. }
  407. else
  408. {
  409. if (dto.UserId == 21) //21==张海麟
  410. {
  411. salary = salary + pm_WageSheet.Mealsupplement + pm_WageSheet.OtherHandle;
  412. }
  413. else
  414. {
  415. salary = PayrollComputation.ConvertToDecimal(salary / pm_WageSheet.WorkDays * pm_WageSheet.RegularDays + pm_WageSheet.Mealsupplement + pm_WageSheet.OtherHandle);
  416. }
  417. }
  418. decimal actualTotal = salary - totalDeduction;
  419. pm_WageSheet.Should = salary;
  420. pm_WageSheet.TotalDeductions = totalDeduction;
  421. pm_WageSheet.TotalRealHair = actualTotal - pm_WageSheet.WithholdingTax;
  422. pm_WageSheet.AfterTax = actualTotal - pm_WageSheet.WithholdingTax;
  423. #endregion
  424. _result = await _wageSheetRep.Post_WageSheet_AddOrEditAsync(dto, pm_WageSheet);
  425. if (_result.Code != 0)
  426. {
  427. return Ok(JsonView(false, _result.Msg));
  428. }
  429. }
  430. catch (Exception ex)
  431. {
  432. return Ok(JsonView(false, ex.Message));
  433. }
  434. return Ok(JsonView(true, _result.Msg, _result.Data));
  435. }
  436. /// <summary>
  437. /// 计算工资
  438. /// </summary>
  439. /// <returns></returns>
  440. [HttpPost]
  441. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  442. public async Task<IActionResult> SalaryCalculatorAsync(SalaryCalculatorDto dto)
  443. {
  444. Result result = new();
  445. Stopwatch sw = new();
  446. sw.Start();
  447. //参数处理
  448. string ymFormat = "yyyy-MM";
  449. string dtFormat = "yyyy-MM-dd";
  450. bool yearMonthDtIsValid = DateTime.TryParseExact(dto.yearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime yearMonthDt);
  451. bool startDtIsValid = DateTime.TryParseExact(dto.startDt, dtFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime startDt);
  452. bool endDtIsValid = DateTime.TryParseExact(dto.endDt, dtFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime endDt);
  453. if (!yearMonthDtIsValid) return Ok(JsonView(false, "年月格式错误!正确时间格式:yyyy-MM "));
  454. if (!startDtIsValid) return Ok(JsonView(false, "开始日期格式错误!正确时间格式:yyyy-MM-dd "));
  455. if (!endDtIsValid) return Ok(JsonView(false, "结束格式错误!正确时间格式:yyyy-MM-dd "));
  456. string thisYearMonth = dto.yearMonth;
  457. string preYearMonth = yearMonthDt.AddMonths(-1).ToString("yyyy-MM");
  458. //计算本月工资起止时间 比如是2月的1号-28号,那就是2月1号的零点到3月1号的零点
  459. DateTime thisStartDt = startDt;
  460. DateTime thisEndDt = endDt; //
  461. //本月工资是否有数据 有数据则不计算
  462. result = await _wageSheetRep.Get_WageSheet_ListByYearMonthAsync(thisYearMonth);
  463. if (result.Code == 0)
  464. {
  465. return Ok(JsonView(false, thisYearMonth + " 工资数据已存在,若无人员工资请手动添加!"));
  466. }
  467. //获取上个月工资信息
  468. List<Pm_WageSheet> preWageSheetItems = await _wageSheetRep._sqlSugar.Queryable<Pm_WageSheet>().Where(it => it.IsDel == 0 && it.YearMonth == preYearMonth).ToListAsync();
  469. preWageSheetItems = preWageSheetItems.OrderBy(it => it.UserId).ToList();
  470. if (preWageSheetItems.Count <= 0)
  471. {
  472. return Ok(JsonView(false, thisYearMonth + " 上月工资数据不存在,请手动添加!"));
  473. }
  474. //处理上个月同月同人 多条数据
  475. List<Pm_WageSheet> preWageSheetItems1 = new();
  476. //preWageSheetItems1 = preWageSheetItems.GroupBy(it => new { it.YearMonth, it.UserId })
  477. // .Select(it => it.FirstOrDefault(item => item.Basic != 0))
  478. // .ToList();
  479. preWageSheetItems1 = preWageSheetItems
  480. .GroupBy(it => new { it.YearMonth, it.UserId })
  481. .Select(it => it.FirstOrDefault(item => item.Basic != 0))
  482. .Where(it => it != null)
  483. .ToList()!;
  484. //获取OA系统内所有用户
  485. List<UserNameView> userNames = _usersRep._sqlSugar.SqlQueryable<UserNameView>("Select Id,CnName From Sys_Users").ToList();
  486. List<Pm_WageSheet> wageSheets = new();
  487. _result = await PayrollComputation.SalaryCalculatorAsync(preWageSheetItems1, userNames, dto.UserId, thisYearMonth, thisStartDt, thisEndDt);
  488. #region 批量添加
  489. if (_result.Code != 0)
  490. {
  491. return Ok(JsonView(false, _result.Msg));
  492. }
  493. wageSheets = _result.Data;
  494. var add = await _wageSheetRep._sqlSugar.Insertable(wageSheets).ExecuteCommandAsync();
  495. if (add <= 0)
  496. {
  497. return Ok(JsonView(false, "操作失败!"));
  498. }
  499. #endregion
  500. #region 处理返回数据
  501. //List <WageSheetItemInfoView> wageSheetItems = new List<WageSheetItemInfoView>();
  502. //wageSheetItems = _mapper.Map<List<WageSheetItemInfoView>>(wageSheets);
  503. //wageSheetItems = wageSheetItems.Select(it =>
  504. // {
  505. // UserNameView? uName1 = new UserNameView();
  506. // UserNameView? uName2 = new UserNameView();
  507. // uName1 = userNames.Where(it1 => it.UserId == it1.Id).FirstOrDefault();
  508. // if (uName1 != null) it.Name = uName1.CnName;
  509. // uName2 = userNames.Where(it1 => it.LastUpdateUserId == it1.Id).FirstOrDefault();
  510. // if (uName2 != null) it.LastUpdateUserName = uName2.CnName;
  511. // return it; }
  512. // ).ToList();
  513. #endregion
  514. sw.Stop();
  515. return Ok(JsonView(true, "操作成功! 耗时:" + (sw.ElapsedMilliseconds / 1000) + "s"));
  516. }
  517. /// <summary>
  518. /// 计算工资 By YearMonth And UserId
  519. /// </summary>
  520. /// <returns></returns>
  521. [HttpPost]
  522. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  523. public async Task<IActionResult> SalaryCalculatorSingleAsync(SalaryCalculatorSingleDto dto)
  524. {
  525. Result result = new();
  526. Stopwatch sw = new();
  527. sw.Start();
  528. //参数处理
  529. string ymFormat = "yyyy-MM";
  530. string ymdFormat = "yyyy-MM-dd";
  531. bool yearMonthDtIsValid = DateTime.TryParseExact(dto.YearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime yearMonthDt);
  532. bool startDtIsValid = DateTime.TryParseExact(dto.StartDate, ymdFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime startDt);
  533. bool endDtIsValid = DateTime.TryParseExact(dto.EndDate, ymdFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime endDt);
  534. if (!yearMonthDtIsValid) return Ok(JsonView(false, "年月格式错误!正确时间格式:yyyy-MM "));
  535. if (!startDtIsValid) return Ok(JsonView(false, "开始时间格式错误!正确时间格式:yyyy-MM-dd "));
  536. if (!yearMonthDtIsValid) return Ok(JsonView(false, "结束时间格式错误!正确时间格式:yyyy-MM-dd "));
  537. List<Pm_WageSheet> wageSheets = new();
  538. Pm_WageSheet wageSheet = _mapper.Map<Pm_WageSheet>(dto);
  539. Pm_WageSheet wageSheet1 = await _wageSheetRep._sqlSugar.Queryable<Pm_WageSheet>().Where(it => it.UserId == dto.UserId && it.YearMonth == dto.YearMonth && it.StartDate == dto.StartDate && it.EndDate == dto.EndDate).FirstAsync();
  540. if (wageSheet1 != null)
  541. {
  542. wageSheet.Id = wageSheet1.Id;
  543. }
  544. wageSheets.Add(wageSheet);
  545. //获取OA系统内所有用户
  546. List<UserNameView> userNames = _usersRep._sqlSugar.SqlQueryable<UserNameView>("Select Id,CnName From Sys_Users").ToList();
  547. _result = await PayrollComputation.SalaryCalculatorAsync(wageSheets, userNames, dto.UserId, dto.YearMonth, startDt, endDt);
  548. if (_result.Code != 0)
  549. {
  550. return Ok(JsonView(false, _result.Msg));
  551. }
  552. List<Pm_WageSheet> wageSheets1 = new();
  553. wageSheets1 = _result.Data;
  554. #region 处理返回数据
  555. List<WageSheetInfoView> wageSheetItems = new();
  556. wageSheetItems = _mapper.Map<List<WageSheetInfoView>>(wageSheets1);
  557. wageSheetItems = wageSheetItems.Select(it =>
  558. {
  559. UserNameView? uName1 = new();
  560. UserNameView? uName2 = new();
  561. uName1 = userNames.Where(it1 => it.UserId == it1.Id).FirstOrDefault();
  562. if (uName1 != null) it.Name = uName1.CnName;
  563. uName2 = userNames.Where(it1 => it.LastUpdateUserId == it1.Id).FirstOrDefault();
  564. if (uName2 != null) it.LastUpdateUserName = uName2.CnName;
  565. return it;
  566. }
  567. ).ToList();
  568. #endregion
  569. sw.Stop();
  570. return Ok(JsonView(true, "操作成功!耗时:" + (sw.ElapsedMilliseconds / 1000) + "s", wageSheetItems[0]));
  571. }
  572. /// <summary>
  573. /// 导出工资单
  574. /// </summary>
  575. /// <returns></returns>
  576. [HttpPost]
  577. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  578. public async Task<IActionResult> ExportWageCard(string yearMonth)
  579. {
  580. Stopwatch sw = new();
  581. sw.Start();
  582. //参数处理
  583. string ymFormat = "yyyy-MM";
  584. bool yearMonthDtIsValid = DateTime.TryParseExact(yearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out _);
  585. if (!yearMonthDtIsValid) return Ok(JsonView(false, "年月格式错误!正确时间格式:yyyy-MM "));
  586. //公司部门
  587. string sql = string.Format(@"Select row_number() over(order by pm_ws.Id) as Row_Number,
  588. sc.Id as CompanyId,sc.CompanyName,sd.Id as DepId,sd.DepName,
  589. sys_u1.CnName Name,sys_u2.CnName LastUpdateUserName,pm_ws.*
  590. From Pm_WageSheet pm_ws
  591. Left Join Sys_Users sys_u1 On pm_ws.UserId = sys_u1.Id
  592. Left Join Sys_Users sys_u2 On pm_ws.LastUpdateUserId = sys_u2.Id
  593. Left Join Sys_Company sc On sys_u1.companyId = sc.Id
  594. Left Join Sys_Department sd On sys_u1.DepId = sd.Id
  595. Where pm_ws.IsDel = 0 And pm_ws.YearMonth = '{0}'", yearMonth);
  596. var wageSheetList = await _wageSheetRep._sqlSugar.SqlQueryable<ExportWageSheetItemView>(sql).ToListAsync();
  597. if (wageSheetList.Count <= 0)
  598. {
  599. return Ok(JsonView(false, yearMonth + "暂无工资数据!"));
  600. }
  601. decimal SumPrice = 0.00M;
  602. foreach (var item in wageSheetList)
  603. {
  604. SumPrice += item.AfterTax;
  605. }
  606. WorkbookDesigner designer = new()
  607. {
  608. Workbook = new Workbook(AppSettingsHelper.Get("ExcelBasePath") + "Template/工资详细清单.xlsx")
  609. };
  610. designer.Workbook.Worksheets[0].Name = yearMonth + " 工资单";
  611. designer.SetDataSource("WageSheet", wageSheetList);
  612. designer.SetDataSource("YearMonth", yearMonth);
  613. designer.SetDataSource("StartEndDt", wageSheetList[0].StartDate + " - " + wageSheetList[0].EndDate);
  614. designer.SetDataSource("WorkDays", wageSheetList[0].WorkDays);
  615. designer.SetDataSource("SumPrice", SumPrice);
  616. designer.SetDataSource("WageSheetTitle", "工资单");//
  617. designer.Process();
  618. string fileName = "WageCard/" + yearMonth + "_工资单_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx";
  619. string path = AppSettingsHelper.Get("ExcelBasePath");
  620. designer.Workbook.Save(path + fileName);
  621. string excelPath = AppSettingsHelper.Get("ExcelFtpPath") + fileName;
  622. string url = AppSettingsHelper.Get("ExcelBaseUrl");
  623. string fileUrl = url + excelPath;
  624. sw.Stop();
  625. return Ok(JsonView(true, "操作成功!耗时:" + (sw.ElapsedMilliseconds / 1000) + "s", new { FileUrl = fileUrl }));
  626. }
  627. /// <summary>
  628. /// 下载个税模板
  629. /// </summary>
  630. /// <returns></returns>
  631. [HttpPost]
  632. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  633. public async Task<IActionResult> WageSheetTaxTemplate()
  634. {
  635. //string serverUrl = AppSettingsHelper.Get("WageSheetExcelBaseUrl");
  636. var userData = await _usersRep.GetUserNameList(1);
  637. if (userData.Code == 0)
  638. {
  639. var userNames = userData.Data;
  640. List<string> names = new()
  641. {
  642. "管理员",
  643. "国交共享号",
  644. "人事审核号",
  645. "国交主管号"
  646. };
  647. List<TaxTemlateViuw> taxs = new();
  648. List<UserNameView> users = JsonConvert.DeserializeObject<List<UserNameView>>(JsonConvert.SerializeObject(userNames));
  649. foreach (UserNameView item in users)
  650. {
  651. string uName = item.CnName;
  652. if (!names.Contains(uName))
  653. {
  654. taxs.Add(new TaxTemlateViuw { UserName = item.CnName });
  655. }
  656. }
  657. if (taxs.Count > 0)
  658. {
  659. WorkbookDesigner designer = new()
  660. {
  661. Workbook = new Workbook(AppSettingsHelper.Get("ExcelBasePath") + "Template/个税导入模板.xlsx")
  662. };
  663. designer.Workbook.Worksheets[0].Name = "个税模板";
  664. designer.SetDataSource("TaxTemp", taxs);
  665. designer.Process();
  666. string fileName = "WageSheetTaxFile/个税模板" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx";
  667. string path = AppSettingsHelper.Get("ExcelBasePath");
  668. designer.Workbook.Save(path + fileName);
  669. string excelPath = AppSettingsHelper.Get("ExcelFtpPath") + fileName;
  670. string url = AppSettingsHelper.Get("ExcelBaseUrl");
  671. string fileUrl = url + excelPath;
  672. return Ok(JsonView(true, "操作成功!", new { FileUrl = fileUrl }));
  673. }
  674. }
  675. return Ok(JsonView(false, "操作失败!"));
  676. }
  677. /// <summary>
  678. /// 上传个税
  679. /// </summary>
  680. /// <returns></returns>
  681. [HttpPost]
  682. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  683. public async Task<IActionResult> UploadTax(IFormFile file)
  684. {
  685. try
  686. {
  687. var yearMonth = Request.Headers["YearMonth"].ToString();
  688. //string yearMonth = "2023-10";
  689. string ymFormat = "yyyy-MM";
  690. bool yearMonthDtIsValid = DateTime.TryParseExact(yearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime yearMonthDt);
  691. if (!yearMonthDtIsValid) return Ok(JsonView(false, "年月格式错误!正确时间格式:yyyy-MM "));
  692. if (file != null)
  693. {
  694. var fileDir = AppSettingsHelper.Get("WageSheetExcelFptPath");
  695. //文件名称
  696. string projectFileName = file.FileName;
  697. //上传的文件的路径
  698. string filePath = "";
  699. if (!Directory.Exists(fileDir))
  700. {
  701. Directory.CreateDirectory(fileDir);
  702. }
  703. //上传的文件的路径
  704. filePath = fileDir + $@"\{projectFileName}";
  705. if (System.IO.File.Exists(filePath))
  706. {
  707. //删除文件
  708. System.IO.File.Delete(filePath);
  709. }
  710. using (FileStream fs = System.IO.File.Create(filePath))
  711. {
  712. file.CopyTo(fs);
  713. fs.Flush();
  714. }
  715. if (System.IO.File.Exists(filePath))
  716. {
  717. Workbook book = new(filePath);
  718. DataSet dataSet = new();
  719. if (book.Worksheets.Count > 0)
  720. {
  721. var sheet = book.Worksheets[0];
  722. if (sheet != null)
  723. {
  724. // sheets 中的数据必须存在
  725. if (sheet.Cells.MaxDataRow != -1 && sheet.Cells.MaxDataColumn != -1)
  726. {
  727. // 方法 ExportDataTable 的参数说明
  728. // 要导出的第一个单元格的行号。
  729. // 要导出的第一个单元格的列号。
  730. // 要导入的行数。
  731. // 要导入的列数。
  732. // 指示第一行的数据是否导出到DataTable的列名。
  733. DataTable dataTable = sheet.Cells.ExportDataTable(0, 0, sheet.Cells.MaxDataRow + 1, sheet.Cells.MaxDataColumn + 1, true);
  734. dataSet.Tables.Add(dataTable);
  735. DataTable taxData = dataSet.Tables[0];
  736. //公司部门
  737. string sql = string.Format(@"Select row_number() over(order by pm_ws.Id) as Row_Number,
  738. sc.Id as CompanyId,sc.CompanyName,sd.Id as DepId,sd.DepName,
  739. sys_u1.CnName Name,sys_u2.CnName LastUpdateUserName,pm_ws.*
  740. From Pm_WageSheet pm_ws
  741. Left Join Sys_Users sys_u1 On pm_ws.UserId = sys_u1.Id
  742. Left Join Sys_Users sys_u2 On pm_ws.LastUpdateUserId = sys_u2.Id
  743. Left Join Sys_Company sc On sys_u1.companyId = sc.Id
  744. Left Join Sys_Department sd On sys_u1.DepId = sd.Id
  745. Where pm_ws.IsDel = 0 And pm_ws.YearMonth = '{0}'
  746. Order By UserId Asc ", yearMonth);
  747. var wageSheetList = await _wageSheetRep._sqlSugar.SqlQueryable<WageSheetInfoView>(sql).ToListAsync();
  748. if (wageSheetList.Count <= 0)
  749. {
  750. return Ok(JsonView(false, yearMonth + "工资数据不存在,请先添加工资数据!"));
  751. }
  752. for (int i = 0; i < taxData.Rows.Count; i++)
  753. {
  754. string name = taxData.Rows[i][0].ToString().Trim();
  755. List<WageSheetInfoView> wageSheets = new();
  756. wageSheets = wageSheetList.Where(it => it.Name.Equals(name)).ToList();
  757. if (wageSheets.Count > 0)
  758. {
  759. wageSheetList.Where(it => it.Name.Equals(name))
  760. .Select(it =>
  761. {
  762. //修改 绩效不等于0.00M的数据
  763. //decimal oldTax = it.WithholdingTax;
  764. var newTax = Convert.ToDecimal(taxData.Rows[i][1].ToString());
  765. it.WithholdingTax = newTax;
  766. it.TotalRealHair = it.Should - it.TotalDeductions - newTax;
  767. return it;
  768. })
  769. .ToList();
  770. }
  771. }
  772. List<Pm_WageSheet> wageSheets1 = new();
  773. wageSheets1 = _mapper.Map<List<Pm_WageSheet>>(wageSheetList);
  774. var updateStatus = _wageSheetRep._sqlSugar
  775. .Updateable(wageSheets1)
  776. .UpdateColumns(it => new { it.WithholdingTax, it.TotalRealHair })
  777. .ExecuteCommand();
  778. if (updateStatus < 0)
  779. {
  780. return Ok(JsonView(false, "操作失败!"));
  781. }
  782. if (System.IO.File.Exists(filePath))
  783. {
  784. //删除文件
  785. System.IO.File.Delete(filePath);
  786. }
  787. return Ok(JsonView(true, "操作成功!"));
  788. }
  789. }
  790. else
  791. {
  792. return Ok(JsonView(false, "工作薄没有数据!"));
  793. }
  794. }
  795. }
  796. return Ok(JsonView(true, "上传成功!", projectFileName));
  797. }
  798. else
  799. {
  800. return Ok(JsonView(false, "上传失败!"));
  801. }
  802. }
  803. catch (Exception ex)
  804. {
  805. return Ok(JsonView(false, "程序错误!"));
  806. throw;
  807. }
  808. //return Ok(JsonView(true, "操作成功!"));
  809. }
  810. /// <summary>
  811. /// 打卡记录测试
  812. /// </summary>
  813. /// <returns></returns>
  814. [HttpPost]
  815. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  816. public async Task<IActionResult> Test(string startDt, string endDt, int code)
  817. {
  818. UserIdListView userIdListView = await _qiYeWeChatApiService.GetUserIdListAsync();
  819. if (userIdListView.errcode != 0)
  820. {
  821. _result.Msg = "【企业微信】【打卡】【获取员工ID】【Msg】" + userIdListView.errmsg;
  822. return Ok(JsonView(false, _result.Msg));
  823. }
  824. List<string> qyWhchatIdList = new();
  825. //qyWhchatIdList = userIdListView.dept_user.Select(it => it.userid).ToList();
  826. qyWhchatIdList = userIdListView.dept_user.Select(it => it.userid!).ToList();
  827. var data = await _qiYeWeChatApiService.GetCheckinDataAsync(qyWhchatIdList, code, Convert.ToDateTime(startDt), Convert.ToDateTime(endDt));
  828. return Ok(JsonView(true, "操作成功!", data.checkindata));
  829. }
  830. /// <summary>
  831. /// 审批详情
  832. /// </summary>
  833. /// <returns></returns>
  834. [HttpPost]
  835. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  836. public async Task<IActionResult> PostApprovalDetailAsync(string spNo)
  837. {
  838. if (string.IsNullOrEmpty(spNo))
  839. {
  840. return Ok(JsonView(false, "审批单号不能为空!!"));
  841. }
  842. var data = await _qiYeWeChatApiService.GetApprovalDetailAsync(spNo);
  843. return Ok(JsonView(true, "操作成功!", data));
  844. }
  845. #endregion
  846. #region 任务单
  847. /// <summary>
  848. /// 任务分配
  849. /// 基础数据源
  850. /// </summary>
  851. /// <returns></returns>
  852. [HttpPost]
  853. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  854. public async Task<IActionResult> PostTaskAllocationInit(TaskAllocationInitDto dto)
  855. {
  856. #region 参数验证
  857. if (dto.UserId < 1) return Ok(JsonView(false, "员工Id为空"));
  858. if (dto.PageId < 1) dto.PageId = 172; //任务指派Id
  859. #region 页面操作权限验证
  860. PageFunAuthViewBase pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(dto.UserId, dto.PageId);
  861. if (pageFunAuthView.CheckAuth == 0) return Ok(JsonView(false, "您没有查看权限!"));
  862. #endregion
  863. #endregion
  864. var _view = await _taskAllocationRep.Init(dto.PortType, dto.UserId);
  865. if (_view.Code == 0)
  866. {
  867. return Ok(JsonView(true, "查询成功!", _view.Data));
  868. }
  869. return Ok(JsonView(false, _view.Msg));
  870. }
  871. /// <summary>
  872. /// 任务分配
  873. /// page
  874. /// </summary>
  875. /// <returns></returns>
  876. [HttpPost]
  877. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  878. public async Task<IActionResult> PostTaskAllocationPage(TaskAllocationPageDto dto)
  879. {
  880. #region 参数验证
  881. if (dto.UserId < 1) return Ok(JsonView(false, "员工Id为空"));
  882. if (dto.PageId < 1) dto.PageId = 172; //任务指派Id
  883. PageFunAuthViewBase pageFunAuthView = new();
  884. #region 页面操作权限验证
  885. pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(dto.UserId, dto.PageId);
  886. if (pageFunAuthView.CheckAuth == 0) return Ok(JsonView(false, "您没有查看权限!"));
  887. #endregion
  888. #endregion
  889. var groupNames = dto.GroupNames;
  890. var groupArr = Array.Empty<string>();
  891. if (!string.IsNullOrEmpty(groupNames))
  892. {
  893. groupArr = groupNames.Split(',').Select(part => part.Trim()).ToArray();
  894. }
  895. string whereSql = "", currUserName = "";
  896. #region 分页参数处理
  897. //类型处理
  898. if (dto.Type == 0) whereSql = "";
  899. else if (dto.Type == 1) //1 由我指派
  900. {
  901. //whereSql = string.Format(@" And ta.CreateUserId = {0} Or (Select COUNT(1) As PeopleNumber From Pm_TaskRelevanceUser Where IsDel = 0 And ta.Id = TAId And UserId = {0}) > 0", dto.UserId);
  902. }
  903. else if (dto.Type == 2)// 2 指派给我
  904. {
  905. //whereSql = string.Format(@" And (Select COUNT(1) As PeopleNumber From Pm_TaskRelevanceUser Where IsDel = 0 And ta.Id = TAId And UserId = {0}) > 0", dto.UserId);
  906. }
  907. //状态 -1 全部 0 未开始 1 进行中 2 待审核 3 未完成 4 已完成
  908. if (dto.Status == -1) //全部
  909. {
  910. whereSql += "";
  911. }
  912. else
  913. {
  914. whereSql += string.Format(@" And ta.Status = {0} ", dto.Status);
  915. }
  916. //任务类型
  917. if (dto.TaskType == -1) //全部
  918. {
  919. whereSql += "";
  920. }
  921. else
  922. {
  923. whereSql += string.Format(@" And ta.TaskType = {0} ", dto.TaskType);
  924. }
  925. //任务名称
  926. if (!string.IsNullOrEmpty(dto.TaskName))
  927. {
  928. whereSql += string.Format(@" And ta.TaskName Like '%{0}%' ", dto.TaskName);
  929. }
  930. #endregion
  931. var currUserInfo = await _sqlSugar.Queryable<Sys_Users>().FirstAsync(x => x.Id == dto.UserId);
  932. if (currUserInfo != null) currUserName = currUserInfo.CnName;
  933. string pageSql = string.Format(@"Select
  934. ROW_NUMBER() OVER(
  935. ORDER BY
  936. CreateTime Desc
  937. ) AS RowNumber,
  938. *
  939. From
  940. (
  941. Select
  942. ta.Id,
  943. ta.TaskName,
  944. ta.TaskContent,
  945. s.Name as 'TaskTypeName',
  946. ta.TaskPriority,
  947. d.DepName,
  948. di.TeamName,
  949. ta.Status,
  950. ta.PredictBeginTime,
  951. ta.PredictEndTime,
  952. ta.CreateUserId,
  953. u.CnName As CreateUserName,
  954. ta.CreateTime,
  955. ta.Remark,
  956. ta.OverTime,
  957. (
  958. SELECT
  959. STUFF(
  960. (
  961. Select
  962. ',' + u.CnName
  963. From
  964. Pm_TaskRelevanceUser tra
  965. Left Join Sys_Users u On tra.UserId = u.Id
  966. Where
  967. tra.Isdel = 0
  968. And tra.TAId = ta.Id FOR XML PATH('')
  969. ),
  970. 1,
  971. 1,
  972. ''
  973. )
  974. ) As Participant
  975. --,(
  976. -- SELECT
  977. -- STUFF(
  978. -- (
  979. -- Select
  980. -- ',' + u.CnName
  981. -- From
  982. -- Pm_TaskRelevanceUser tra
  983. -- Left Join Sys_Users u On tra.UserId = u.Id
  984. -- Where
  985. -- tra.Isdel = 0
  986. -- And tra.TAId = ta.Id
  987. -- And tra.TaskStatus = 4 FOR XML PATH('')
  988. -- ),
  989. -- 1,
  990. -- 1,
  991. -- ''
  992. -- )
  993. --) As Consummator
  994. From
  995. Pm_TaskAllocation ta
  996. Left Join Sys_Department d On ta.DepId = d.Id
  997. Left Join Grp_DelegationInfo di On ta.DiId = di.Id
  998. Left Join Sys_Users u On ta.CreateUserId = u.Id
  999. left join Sys_SetData s on ta.TaskType = s.Id
  1000. Where
  1001. ta.IsDel = 0 {0}
  1002. ) As temp
  1003. WHERE
  1004. [CreateUserName] like '%{1}%'
  1005. OR [Participant] like '%{2}%' ", whereSql, currUserName, currUserName);
  1006. RefAsync<int> total = 0;
  1007. var _view = await _sqlSugar.SqlQueryable<TaskListView>(pageSql)
  1008. .WhereIF(groupArr.Any(), x => groupArr.Contains(x.TeamName))
  1009. .ToPageListAsync(dto.PageIndex, dto.PageSize, total);//ToPageAsync
  1010. foreach (var item in _view)
  1011. {
  1012. //设置编辑权限
  1013. (bool editPerm, string msg1) = _taskAllocationRep.EditPerm(item.Id, dto.UserId);
  1014. item.EditPerm = editPerm;
  1015. //设置任务确认权限
  1016. if (item.CreateUserId == dto.UserId) item.TaskCompletedPerm = true;
  1017. //设置已知晓权限
  1018. (bool havePerm, string msg3) = _taskAllocationRep.TaskHavePerm(item.Id, dto.UserId);
  1019. item.TaskHavePerm = havePerm;
  1020. //设置详情权限
  1021. item.TaskDetailsPerm = _sqlSugar.Queryable<Pm_TaskRelevanceUser>()
  1022. .Where(it => it.IsDel == 0 && it.TAId == item.Id && (it.UserId == dto.UserId || it.CreateUserId == dto.UserId))
  1023. .Any();
  1024. }
  1025. return Ok(JsonView(true, "查询成功!", _view, total));
  1026. }
  1027. /// <summary>
  1028. /// 任务分配
  1029. /// 详情
  1030. /// </summary>
  1031. /// <returns></returns>
  1032. [HttpPost]
  1033. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1034. public async Task<IActionResult> PostTaskAllocationDetails(TaskAllocationDetailsDto dto)
  1035. {
  1036. #region 参数验证
  1037. if (dto.UserId < 1) return Ok(JsonView(false, "员工Id为空"));
  1038. if (dto.PageId < 1) dto.PageId = 172; //任务指派Id
  1039. #region 页面操作权限验证
  1040. PageFunAuthViewBase pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(dto.UserId, dto.PageId);
  1041. if (pageFunAuthView.CheckAuth == 0) return Ok(JsonView(false, "您没有查看权限!"));
  1042. #endregion
  1043. #endregion
  1044. var _view = await _taskAllocationRep.Details(dto.PortType, dto.Id);
  1045. if (_view.Code == 0)
  1046. {
  1047. return Ok(JsonView(true, "查询成功!", _view.Data));
  1048. }
  1049. return Ok(JsonView(false, _view.Msg));
  1050. }
  1051. /// <summary>
  1052. /// 任务分配 发布任务/修改任务
  1053. /// </summary>
  1054. /// <returns></returns>
  1055. [HttpPost]
  1056. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1057. public async Task<IActionResult> PostTaskAllocationAddOrEdit(TaskAllocationAddOrEditDto dto)
  1058. {
  1059. #region 参数验证
  1060. if (dto.UserId < 1) return Ok(JsonView(false, "员工Id为空"));
  1061. if (dto.PageId < 1) dto.PageId = 172; //任务指派Id
  1062. PageFunAuthViewBase pageFunAuthView = new();
  1063. #region 页面操作权限验证
  1064. pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(dto.UserId, dto.PageId);
  1065. if (dto.Id == 0)
  1066. {
  1067. if (pageFunAuthView.AddAuth == 0) return Ok(JsonView(false, "您没有添加权限!"));
  1068. }
  1069. else if (dto.Id > 0)
  1070. {
  1071. if (pageFunAuthView.EditAuth == 0) return Ok(JsonView(false, "您没有编辑权限!"));
  1072. }
  1073. #endregion
  1074. #endregion
  1075. var _view = await _taskAllocationRep.AddOrEdit(dto);
  1076. if (_view.Code == 0)
  1077. {
  1078. if (dto.Id == 0) //添加提示任务单创建
  1079. {
  1080. string title = $"[{dto.TaskName}] 任务新建成功!";
  1081. string content = $"[{dto.TaskName}] 任务新建成功,请前往任务页面查看详情!";
  1082. await GeneralMethod.MessageIssueAndNotification(MessageTypeEnum.TaskProgressUpdate, title, content, dto.UserIds);
  1083. var userIds = dto.UserIds.Select(x => x.ToString()).ToList();
  1084. await AppNoticeLibrary.SendUserMsg_Task_ToUser(userIds, dto.DiId, title, dto.UserId);
  1085. }
  1086. return Ok(JsonView(true, "操作成功!"));
  1087. }
  1088. return Ok(JsonView(false, _view.Msg));
  1089. }
  1090. /// <summary>
  1091. /// 任务分配 状态任务归属人详情
  1092. /// </summary>
  1093. /// <returns></returns>
  1094. [HttpPost]
  1095. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1096. public async Task<IActionResult> PostTaskAllocationTaskerDetails(TaskerDetailsDto dto)
  1097. {
  1098. var _view = await _taskAllocationRep.TaskerDetails(dto.Id);
  1099. if (_view.Code == 0)
  1100. {
  1101. return Ok(JsonView(true, "操作成功!", _view.Data));
  1102. }
  1103. return Ok(JsonView(false, _view.Msg));
  1104. }
  1105. /// <summary>
  1106. /// 任务分配 任务归属人设置知晓状态
  1107. /// </summary>
  1108. /// <returns></returns>
  1109. [HttpPost]
  1110. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1111. public async Task<IActionResult> PostTaskAllocationSetHaveStatus(TaskAllocationSetHaveStatusDto dto)
  1112. {
  1113. var _view = await _taskAllocationRep.TaskSetHaveStatus(dto.Id, dto.UserId);
  1114. if (_view.Code == 0)
  1115. {
  1116. //发送消息
  1117. var taskData = _taskAllocationRep._sqlSugar.Queryable<Pm_TaskAllocation>().Where(it => it.Id == dto.Id).First();
  1118. if (taskData != null)
  1119. {
  1120. var userName = _taskAllocationRep._sqlSugar.Queryable<Sys_Users>().Where(it => it.Id == taskData.CreateUserId).Select(it => it.CnName).First();
  1121. var taskUserIds = _taskAllocationRep._sqlSugar.Queryable<Pm_TaskRelevanceUser>().Where(it => it.TAId == dto.Id).Select(x => x.UserId).ToList();
  1122. var taskUserIdStrs = taskUserIds.Select(x => x.ToString()).ToList();
  1123. string title = $"[{taskData.TaskName}] 进度更新!";
  1124. string conten_createUser = $"[{userName}] 已知晓任务.若需查看,请前往任务页面查看详情!";
  1125. string qw_conten_createUser = $"[{userName}] 已知晓任务!";
  1126. await GeneralMethod.MessageIssueAndNotification(MessageTypeEnum.TaskProgressUpdate, title, conten_createUser, new List<int>() { taskData.CreateUserId }); //创建人发送消息
  1127. await AppNoticeLibrary.SendUserMsg_Task_ToUser(new List<string>() { taskData.CreateUserId.ToString() }, taskData.DiId, qw_conten_createUser, taskData.CreateUserId);
  1128. string content = $"请注意任务完成时间!若需查看,请前往任务页面查看详情!";
  1129. string yw_content = $"请注意任务完成时间!";
  1130. await GeneralMethod.MessageIssueAndNotification(MessageTypeEnum.TaskProgressUpdate, title, content, taskUserIds); //设置任务人 发送消息
  1131. await AppNoticeLibrary.SendUserMsg_Task_ToUser(taskUserIdStrs, taskData.DiId, yw_content, taskData.CreateUserId);
  1132. }
  1133. return Ok(JsonView(true, "操作成功!"));
  1134. }
  1135. return Ok(JsonView(false, _view.Msg));
  1136. }
  1137. /// <summary>
  1138. /// 任务分配 设置完成状态
  1139. /// </summary>
  1140. /// <returns></returns>
  1141. [HttpPost]
  1142. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1143. public async Task<IActionResult> PostTaskAllocationSetAuditStatus(TaskAllocationSetAuditStatusDto dto)
  1144. {
  1145. var _view = _taskAllocationRep.TaskSetAuditStatus(dto.Id, dto.UserId, dto.OverTime,dto.Remark);
  1146. if (_view.Code == 0)
  1147. {
  1148. //发送消息
  1149. var taskUserInfo = _taskAllocationRep._sqlSugar.Queryable<Pm_TaskRelevanceUser>().Where(it => it.TAId == dto.Id).First();
  1150. if (taskUserInfo != null)
  1151. {
  1152. var taskInfo = _taskAllocationRep._sqlSugar.Queryable<Pm_TaskAllocation>().Where(it => it.Id == dto.Id).First();
  1153. if (taskInfo != null)
  1154. {
  1155. var userIds = _taskAllocationRep._sqlSugar.Queryable<Pm_TaskRelevanceUser>().Where(it => it.TAId == dto.Id).Select(it => it.UserId).ToList();
  1156. string title = $"[{taskInfo.TaskName}] 进度更新!";
  1157. string content = $"任务已完成!若需查看,请前往任务页面查看详情!";
  1158. string yw_content = $"任务已完成!";
  1159. await GeneralMethod.MessageIssueAndNotification(MessageTypeEnum.TaskProgressUpdate, title, content, userIds); //设置任务人 发送消息
  1160. var userIdStrs = userIds.Select(x => x.ToString()).ToList();
  1161. await AppNoticeLibrary.SendUserMsg_Task_ToUser(userIdStrs, taskInfo.DiId, yw_content, taskInfo.CreateUserId);
  1162. }
  1163. }
  1164. return Ok(JsonView(true, "操作成功!"));
  1165. }
  1166. return Ok(JsonView(false, _view.Msg));
  1167. }
  1168. /// <summary>
  1169. /// 任务分配
  1170. /// 任务发布者 任务终止
  1171. /// </summary>
  1172. /// <returns></returns>
  1173. [HttpPost]
  1174. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1175. public async Task<IActionResult> PostTaskAllocationTermination(TaskAllocationConfirmCompletionDto dto)
  1176. {
  1177. #region 参数验证
  1178. if (dto.UserId < 1) return Ok(JsonView(false, "员工Id为空"));
  1179. if (dto.PageId < 1) dto.PageId = 172; //任务指派Id
  1180. #region 页面操作权限验证
  1181. PageFunAuthViewBase pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(dto.UserId, dto.PageId);
  1182. if (pageFunAuthView.EditAuth == 0) return Ok(JsonView(false, "您没有编辑权限!"));
  1183. #endregion
  1184. #endregion
  1185. var _view = await _taskAllocationRep.TaskTermination(dto.Id, dto.UserId);
  1186. if (_view.Code == 0)
  1187. {
  1188. //发送消息
  1189. var taskData = _taskAllocationRep._sqlSugar.Queryable<Pm_TaskAllocation>().Where(it => it.Id == dto.Id).First();
  1190. if (taskData != null)
  1191. {
  1192. var taskUserIds = _taskAllocationRep._sqlSugar.Queryable<Pm_TaskRelevanceUser>().Where(it => it.Id == dto.Id).Select(it => it.UserId).ToList();
  1193. taskUserIds.Add(taskData.CreateUserId);
  1194. string title = $"[{taskData.TaskName}] 进度更新!";
  1195. string content = $"任务已终止!若需查看,请前往任务页面查看详情!";
  1196. string yw_content = $"[{taskData.TaskName}]任务已终止!";
  1197. await GeneralMethod.MessageIssueAndNotification(MessageTypeEnum.TaskProgressUpdate, title, content, taskUserIds); //设置任务人 发送消息
  1198. await AppNoticeLibrary.SendUserMsg_Task_ToUser(taskUserIds.Select(x => x.ToString()).ToList(), taskData.DiId, yw_content, taskData.CreateUserId);
  1199. }
  1200. return Ok(JsonView(true, "操作成功!"));
  1201. }
  1202. return Ok(JsonView(false, _view.Msg));
  1203. }
  1204. /// <summary>
  1205. /// 任务分配
  1206. /// 任务发布者 任务删除
  1207. /// </summary>
  1208. /// <returns></returns>
  1209. [HttpPost]
  1210. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1211. public async Task<IActionResult> PostTaskAllocationDel(TaskAllocationConfirmCompletionDto dto)
  1212. {
  1213. #region 参数验证
  1214. if (dto.UserId < 1) return Ok(JsonView(false, "员工Id为空"));
  1215. if (dto.PageId < 1) dto.PageId = 172; //任务指派Id
  1216. #region 页面操作权限验证
  1217. PageFunAuthViewBase pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(dto.UserId, dto.PageId);
  1218. if (pageFunAuthView.DeleteAuth == 0) return Ok(JsonView(false, "您没有删除权限!"));
  1219. #endregion
  1220. #endregion
  1221. var _view = await _taskAllocationRep.TaskDel(dto.Id, dto.UserId);
  1222. if (_view.Code == 0)
  1223. {
  1224. return Ok(JsonView(true, "操作成功!"));
  1225. }
  1226. return Ok(JsonView(false, _view.Msg));
  1227. }
  1228. #endregion
  1229. #region 团组状态通知
  1230. // ///// <summary>
  1231. // ///// 测试
  1232. // ///// </summary>
  1233. // ///// <param name="dto"></param>
  1234. // ///// <returns></returns>
  1235. // [HttpPost]
  1236. // [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1237. // public async Task<IActionResult> Test_QIYEWX(PostTourClientListDownloadFile dto)
  1238. // {
  1239. // // //List<string> templist = new List<string>() { 234.ToString() };
  1240. // // //await AppNoticeLibrary.SendUserMsg_GroupStatus_AuditFee(14090, templist, QiyeWeChatEnum.TestChat);
  1241. // // //await AppNoticeLibrary.SendUserMsg_GroupShare_ToJob(dto.DiId); ;
  1242. // // //DeleReminderMessage.PostMessageByWebhook();
  1243. // // //GroupStatus_UserSimplelistView list = await _qiYeWeChatApiService.GroupStatus_GetUserList();
  1244. // // //创建群聊
  1245. // // //List<string> userList1 = new List<string>() { "Feint", "amy.zhu@pan-american-intl.com", "judy.zeng@pan-american-intl.com", "FuHongJin" };
  1246. // // //List<string> userList2 = new List<string>() { "Feint", "ZhaoYaQi", "LiaoWenYa" };
  1247. // // List<string> userList3 = new List<string>() { "rongfeng.yuan", "zhaiyang" };
  1248. // // //GroupStatus_CreateChatView rst1 = await _qiYeWeChatApiService.GroupStatus_CreateChat("团组通知", "Feint", userList1, "CaiWuChat01");
  1249. // // //GroupStatus_CreateChatView rst2 = await _qiYeWeChatApiService.GroupStatus_CreateChat("OA通知-国交部", "Feint", userList2, "GuoJiaoLeader01");
  1250. // // GroupStatus_CreateChatView rst3 = await _qiYeWeChatApiService.GroupStatus_CreateChat("OA通知-团组超支费用通知", "rongfeng.yuan", userList3, "ExpenseOverrunsChat01");
  1251. // // GroupStatus_CreateChatView rst4 = await _qiYeWeChatApiService.GroupStatus_CreateChat("OA通知-团组超支费用通知(商邀)", "rongfeng.yuan", userList3, "ExpenseOverrunsChat02");
  1252. // // //推送消息(模板)
  1253. // // //List<string> userList = new List<string>() { "Feint", "huaju.liu", "johnny.yang@pan-american-intl.com" };
  1254. // // //List<string> userList = new List<string>() { "Feint", "johnny.yang@pan-american-intl.com" };
  1255. // // GroupStatus_CreateChatView rst1 = await _qiYeWeChatApiService.GroupStatus_CreateChat("团组费用提示", "Feint", userList, "GuoJiaoChat01");
  1256. // // //团组出发
  1257. // // //AppNoticeLibrary.SendUserMsg_GroupStatus_Create(2358, userList);
  1258. // // //费用审核
  1259. // // //AppNoticeLibrary.SendChatMsg_GroupStatus_ApplyFee(dto.DiId, QiyeWeChatEnum.TestChat);
  1260. // // //费用审核结果通知(通过情况下发送财务群)
  1261. // // //List<string> userList = new List<string>() { "234" };
  1262. // // //AppNoticeLibrary.SendUserMsg_GroupStatus_AuditFee(dto.DiId, userList, QiyeWeChatEnum.CaiWuChat02);
  1263. // // //4、财务付款时:(1)对应版块人员【单独发,状态为已付款才发】
  1264. // // //AppNoticeLibrary.SendUserMsg_GroupStatus_PayResult(dto.DiId, userList);
  1265. // // //团组提醒财务群
  1266. // // //DateTime dtNow = DateTime.Now;
  1267. // // //List<Grp_DelegationInfo> listSource = _usersRep.Query<Grp_DelegationInfo>(s => s.IsDel == 0 && s.VisitStartDate >= dtNow).Take(3).ToList();
  1268. // // //List<Grp_DelegationInfo> listAdd7day = _usersRep.Query<Grp_DelegationInfo>(s => s.IsDel == 0 && s.VisitStartDate >= dtNow).Take(3).ToList();
  1269. // // //List<Grp_DelegationInfo> listAdd3day = new List<Grp_DelegationInfo>() { };
  1270. // //机票预算群通知
  1271. // await AppNoticeLibrary.SendChatMsg_ExpenseOverrunsChat(dto.DiId, dto.UserId, QiyeWeChatEnum.ExpenseOverrunsChat01);
  1272. // await AppNoticeLibrary.SendChatMsg_ExpenseOverrunsChat(dto.DiId, dto.UserId, QiyeWeChatEnum.ExpenseOverrunsChat02);
  1273. // // //日付申请提醒财务群
  1274. // // //AppNoticeLibrary.DailyPayReminders_Create_ToCaiwuChat(dto.DiId, QiyeWeChatEnum.CaiWuChat02);
  1275. // // //日付申请结果推送用户、成功通知财务群
  1276. // // //AppNoticeLibrary.DailyPayReminder_Audit_ToUser(dto.DiId, userList, QiyeWeChatEnum.TestChat);
  1277. // // //AppNoticeLibrary.DailyPayReminder_Pay_ToUser(dto.DiId, userList);
  1278. // // //string q = "";
  1279. // // DeleReminderMessage.PostMessageByWebhook_CRMStatistics();
  1280. // return Ok(JsonView(true, "操作成功!"));
  1281. // }
  1282. #endregion
  1283. #region 物资进销存
  1284. /// <summary>
  1285. /// 物资进销存
  1286. /// 基础数据类型
  1287. /// </summary>
  1288. /// <returns></returns>
  1289. [HttpGet]
  1290. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1291. public async Task<IActionResult> GoodsInitDataSource()
  1292. {
  1293. return Ok(await _goodsRep.InitDataSource());
  1294. }
  1295. /// <summary>
  1296. /// 物资进销存
  1297. /// 基础数据类型 --> 团组名称列表 分页
  1298. /// </summary>
  1299. /// <returns></returns>
  1300. [HttpPost]
  1301. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1302. public async Task<IActionResult> GoodsGroupNameInit(QueryGroupListOffsetDto dto)
  1303. {
  1304. var watch = new Stopwatch();
  1305. watch.Start();
  1306. var countyDatas = await _sqlSugar
  1307. .Queryable<Grp_DelegationInfo>()
  1308. .Where((di) => di.IsDel == 0 && !string.IsNullOrWhiteSpace(di.TeamName))
  1309. .OrderBy((di) => new { id = SqlFunc.Desc(di.Id) })
  1310. .Select((di) => new { id = di.Id, name = di.TeamName })
  1311. .ToListAsync();
  1312. countyDatas.Insert(0, new { id = 0, name = "其他物资(公司内部物资)" });
  1313. countyDatas.Insert(0, new { id = -1, name = "拜访客户所使用的物资" });
  1314. countyDatas.Insert(0, new { id = -2, name = "库存调整" });
  1315. countyDatas.Insert(0, new { id = -3, name = "全部团组" });
  1316. var total = countyDatas.Count;
  1317. countyDatas = countyDatas.WhereIF(!string.IsNullOrEmpty(dto.Search), x => x.name.Contains(dto.Search)).ToList();
  1318. countyDatas = countyDatas
  1319. .Skip((dto.PageIndex - 1) * dto.PageSize)
  1320. .Take(dto.PageSize)
  1321. .ToList();
  1322. watch.Stop();
  1323. return Ok(JsonView(true, $"{MsgTips.Succeed},耗时 {watch.ElapsedMilliseconds} ms", countyDatas, total));
  1324. }
  1325. /// <summary>
  1326. /// 物资进销存
  1327. /// 物品 列表
  1328. /// </summary>
  1329. /// <returns></returns>
  1330. [HttpPost]
  1331. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1332. public async Task<IActionResult> GoodsList(GoodsListDto dto)
  1333. {
  1334. if (dto.PortType < 1 || dto.PortType > 3) return Ok(JsonView(false, MsgTips.Port));
  1335. if (dto.PageIndex < 1 || dto.PageSize < 1) return Ok(JsonView(false, MsgTips.PageIndex));
  1336. return Ok(await _goodsRep.GoodsList(dto));
  1337. }
  1338. /// <summary>
  1339. /// 物资进销存
  1340. /// 物品 详情
  1341. /// </summary>
  1342. /// <returns></returns>
  1343. [HttpGet()]
  1344. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1345. public async Task<IActionResult> GoodsInfo(int portType, int id)
  1346. {
  1347. if (portType < 1 || portType > 3) return Ok(JsonView(false, MsgTips.Port));
  1348. if (id < 1) return Ok(JsonView(false, MsgTips.Id));
  1349. return Ok(await _goodsRep.GoodsInfo(portType, id));
  1350. }
  1351. /// <summary>
  1352. /// 物资进销存
  1353. /// 物品 OP(Add OR Edit)
  1354. /// </summary>
  1355. /// <returns></returns>
  1356. [HttpPost]
  1357. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1358. public async Task<IActionResult> GoodsOP(GoodsOpDto dto)
  1359. {
  1360. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1361. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1362. var validator = new GoodsOPDTOValidator();
  1363. var validatorRes = await validator.ValidateAsync(dto);
  1364. if (!validatorRes.IsValid)
  1365. {
  1366. var sb = new StringBuilder();
  1367. foreach (var error in validatorRes.Errors) sb.AppendLine(error.ErrorMessage);
  1368. return Ok(JsonView(false, sb.ToString()));
  1369. }
  1370. return Ok(await _goodsRep.GoodsOp(dto, currUserInfo.UserId));
  1371. }
  1372. /// <summary>
  1373. /// 物资进销存
  1374. /// 物品 Del
  1375. /// </summary>
  1376. /// <returns></returns>
  1377. [HttpDelete("{id}")]
  1378. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1379. public async Task<IActionResult> GoodsDel(int id)
  1380. {
  1381. if (id < 1) return Ok(JsonView(false, MsgTips.Id));
  1382. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1383. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1384. return Ok(await _goodsRep.GoodsDel(id, currUserInfo.UserId));
  1385. }
  1386. /// <summary>
  1387. /// 物资进销存(审核,需传token)
  1388. /// 入库 列表
  1389. /// </summary>
  1390. /// <returns></returns>
  1391. [HttpPost]
  1392. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1393. public async Task<IActionResult> GoodsStorageList(GoodsStorageListDto dto)
  1394. {
  1395. //var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1396. //if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1397. if (dto.PortType < 1 || dto.PortType > 3) return Ok(JsonView(false, MsgTips.Port));
  1398. if (dto.PageIndex < 1 || dto.PageSize < 1) return Ok(JsonView(false, MsgTips.PageIndex));
  1399. return Ok(await _goodsRep.GoodsStorageList(dto));
  1400. //return Ok(await _goodsRep.GoodsStorageList(dto, 0));
  1401. }
  1402. /// <summary>
  1403. /// 物资进销存
  1404. /// 入库 详情
  1405. /// </summary>
  1406. /// <returns></returns>
  1407. [HttpGet("{id}")]
  1408. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1409. public async Task<IActionResult> GoodsStorageInfo([FromQuery] int portType, int id)
  1410. {
  1411. if (portType < 1 || portType > 3) return Ok(JsonView(false, MsgTips.Port));
  1412. if (id < 1) return Ok(JsonView(false, MsgTips.Id));
  1413. return Ok(await _goodsRep.GoodsStorageInfo(portType, id));
  1414. }
  1415. /// <summary>
  1416. /// 物资进销存
  1417. /// 入库 OP(Add OR Edit)
  1418. /// </summary>
  1419. /// <returns></returns>
  1420. [HttpPost]
  1421. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1422. public async Task<IActionResult> GoodsStorageOp(GoodsStorageOpDto dto)
  1423. {
  1424. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1425. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1426. var validator = new GoodsStorageOpDtoValidator();
  1427. var validatorRes = await validator.ValidateAsync(dto);
  1428. if (!validatorRes.IsValid)
  1429. {
  1430. var sb = new StringBuilder();
  1431. foreach (var error in validatorRes.Errors) sb.AppendLine(error.ErrorMessage);
  1432. return Ok(JsonView(false, sb.ToString()));
  1433. }
  1434. return Ok(await _goodsRep.GoodsStorageOp(dto, currUserInfo.UserId));
  1435. }
  1436. /// <summary>
  1437. /// 物资进销存
  1438. /// 入库确认状态变更
  1439. /// </summary>
  1440. /// <returns></returns>
  1441. [HttpPost]
  1442. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1443. public async Task<IActionResult> GoodsStorageConfirmStatusChange(GoodsStorageConfirmDto dto)
  1444. {
  1445. if (dto.Id < 1) return Ok(JsonView(false, MsgTips.Id));
  1446. if (!Enum.IsDefined(typeof(GoodsAuditDepEnum), (int)dto.AuditDep))
  1447. {
  1448. return Ok(JsonView(false, $"审核部门类型超出可用范围!"));
  1449. }
  1450. if (!Enum.IsDefined(typeof(GoodsConfirmEnum), (int)dto.ConfirmStatus))
  1451. {
  1452. return Ok(JsonView(false, $"入库审核状态超出可用范围!"));
  1453. }
  1454. if (dto.ConfirmStatus == GoodsConfirmEnum.WaitConfirm || dto.ConfirmStatus == GoodsConfirmEnum.PartConfirmed)
  1455. {
  1456. return Ok(JsonView(false, $"入库审核状态(待确认、部分确认)不可用!"));
  1457. }
  1458. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1459. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1460. return Ok(await _goodsRep.GoodsStorageConfirmStatusChange(dto, currUserInfo.UserId));
  1461. }
  1462. /// <summary>
  1463. /// 物资进销存
  1464. /// 入库 Del
  1465. /// </summary>
  1466. /// <returns></returns>
  1467. [HttpDelete("{id}")]
  1468. //[Authorize]
  1469. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1470. public async Task<IActionResult> GoodsStorageDel(int id)
  1471. {
  1472. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1473. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1474. if (id < 1) return Ok(JsonView(false, MsgTips.Id));
  1475. return Ok(await _goodsRep.GoodsStorageDel(id, currUserInfo.UserId));
  1476. }
  1477. /// <summary>
  1478. /// 物资进销存
  1479. /// 入库 excelDownload
  1480. /// </summary>
  1481. /// <returns></returns>
  1482. [HttpPost]
  1483. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1484. public async Task<IActionResult> GoodsStorageExcelDownload()
  1485. {
  1486. //token验证
  1487. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1488. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1489. //文件下载权限验证
  1490. int userId = currUserInfo.UserId,
  1491. pageId = 191;
  1492. #region 页面操作权限验证
  1493. PageFunAuthViewBase pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(userId, pageId);
  1494. if (pageFunAuthView.FilesDownloadAuth == 0) return Ok(JsonView(false, "您未分配文件下载权限!"));
  1495. #endregion
  1496. return Ok(await _goodsRep.GoodsStorageExcelDownload());
  1497. }
  1498. /// <summary>
  1499. /// 物资进销存
  1500. /// 领用记录 List
  1501. /// </summary>
  1502. /// <returns></returns>
  1503. [HttpPost]
  1504. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1505. public async Task<IActionResult> GoodsReceiveList(GoodsReceiveListDTO dto)
  1506. {
  1507. //token验证
  1508. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1509. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1510. if (dto.PortType < 1 || dto.PortType > 3) return Ok(JsonView(false, MsgTips.Port));
  1511. if (dto.PageIndex < 1 || dto.PageSize < 1) return Ok(JsonView(false, MsgTips.PageIndex));
  1512. //文件下载权限验证
  1513. int userId = currUserInfo.UserId,
  1514. pageId = 191;
  1515. if (dto.IsExcelDownload)
  1516. {
  1517. if (userId < 1) return Ok(JsonView(false, "excel导出时,需传入正确的当前操作人UserId!"));
  1518. ;
  1519. #region 页面操作权限验证
  1520. PageFunAuthViewBase pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(userId, pageId);
  1521. if (pageFunAuthView.FilesDownloadAuth == 0) return Ok(JsonView(false, "您未分配文件下载权限!"));
  1522. #endregion
  1523. }
  1524. dto.CurrUserId = currUserInfo.UserId;
  1525. return Ok(await _goodsRep.GoodsReceiveList(dto));
  1526. }
  1527. /// <summary>
  1528. /// 物资进销存
  1529. /// 领用 审核 List
  1530. /// </summary>
  1531. /// <returns></returns>
  1532. [HttpPost]
  1533. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1534. public async Task<IActionResult> GoodsReceiveAuditList(GoodsReceiveAuditListDTO dto)
  1535. {
  1536. //token验证
  1537. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1538. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1539. if (dto.PortType < 1 || dto.PortType > 3) return Ok(JsonView(false, MsgTips.Port));
  1540. if (dto.PageIndex < 1 || dto.PageSize < 1) return Ok(JsonView(false, MsgTips.PageIndex));
  1541. dto.CurrUserId = currUserInfo.UserId;
  1542. return Ok(await _goodsRep.GoodsReceiveAuditList(dto));
  1543. }
  1544. /// <summary>
  1545. /// 物资进销存
  1546. /// 领用详情
  1547. /// </summary>
  1548. /// <returns></returns>
  1549. [HttpGet("{id}")]
  1550. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1551. public async Task<IActionResult> GoodsReceiveInfo([FromQuery] int portType, int id)
  1552. {
  1553. if (portType < 1 || portType > 3) return Ok(JsonView(false, MsgTips.Port));
  1554. if (id < 1) return Ok(JsonView(false, MsgTips.Id));
  1555. return Ok(await _goodsRep.GoodsReceiveInfo(portType, id));
  1556. }
  1557. /// <summary>
  1558. /// 物资进销存
  1559. /// 领用 OP
  1560. /// </summary>
  1561. /// <returns></returns>
  1562. [HttpPost]
  1563. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1564. public async Task<IActionResult> GoodsReceiveOP(GoodsReceiveOpDto dto)
  1565. {
  1566. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1567. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1568. var validator = new GoodsReceiveOpDtoValidator();
  1569. var validatorRes = await validator.ValidateAsync(dto);
  1570. if (!validatorRes.IsValid)
  1571. {
  1572. var sb = new StringBuilder();
  1573. foreach (var error in validatorRes.Errors) sb.AppendLine(error.ErrorMessage);
  1574. return Ok(JsonView(false, sb.ToString()));
  1575. }
  1576. //Return Ok(await _goodsRep.GoodsReceiveOp(dto, currUserInfo.UserId));
  1577. //TODO:调试完成后 单个领用使用批量领用方法
  1578. var receiveDetails = new GoodsReceiveDetailsView[] {
  1579. new () {
  1580. Id = 0,
  1581. GoodsId = dto.GoodsId,
  1582. Quantity = dto.Quantity,
  1583. Remark = dto.Remark
  1584. }
  1585. };
  1586. var newDto = new GoodsReceiveBatchOpDto()
  1587. {
  1588. Id = dto.Id,
  1589. GroupId = dto.GroupId,
  1590. ReceiveDetails = receiveDetails,
  1591. Reason = dto.Reason,
  1592. Remark = dto.Remark,
  1593. IsReplace = dto.IsReplace,
  1594. CurrUserId = currUserInfo.UserId,
  1595. };
  1596. var res = await _goodsRep.GoodsReceiveBatchOpAsync(newDto);
  1597. //企业微信消息通知
  1598. if (res.Code == StatusCodes.Status200OK && dto.Id < 1)
  1599. {
  1600. var signProperty = res.Data?.GetType().GetProperty("sign");
  1601. int sign = signProperty != null && signProperty.GetValue(res.Data, null) is int value ? value : 0;
  1602. //物资领用通知
  1603. await AppNoticeLibrary.SendUserMsg_CompanyShare_ToHR(sign, dto.GroupId, currUserInfo.UserId);
  1604. }
  1605. return Ok(res);
  1606. }
  1607. /// <summary>
  1608. /// 物资进销存
  1609. /// 领用 审核
  1610. /// </summary>
  1611. /// <param name="dto"></param>
  1612. /// <returns></returns>
  1613. [HttpPost]
  1614. [ApiExplorerSettings(IgnoreApi = true)]
  1615. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1616. public async Task<IActionResult> GoodsReceiveAudit(GoodsReceiveAuditDTO dto)
  1617. {
  1618. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1619. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1620. var validator = new GoodsReceiveAuditDTOValidator();
  1621. var validatorRes = await validator.ValidateAsync(dto);
  1622. if (!validatorRes.IsValid)
  1623. {
  1624. var sb = new StringBuilder();
  1625. foreach (var error in validatorRes.Errors) sb.AppendLine(error.ErrorMessage);
  1626. return Ok(JsonView(false, sb.ToString()));
  1627. }
  1628. int[] idArray = dto.Label
  1629. .Split(',')
  1630. .Select(x =>
  1631. {
  1632. if (int.TryParse(x, out var id)) return id;
  1633. return id;
  1634. })
  1635. .ToArray();
  1636. var view = await _goodsRep.GoodsReceiveAudit(idArray, currUserInfo.UserId, dto.AuditEnum);
  1637. //var view = new JsonView() { Code = StatusCodes.Status200OK};
  1638. //TODO:出库成功 并且是团组相关物资 向团组其他款项添加信息
  1639. #region 出库成功 && 不需要归还的物资(IsReplace == false) && 团组相关物资 向团组其他款项添加信息
  1640. if (view.Code != StatusCodes.Status200OK) return Ok(view);
  1641. if (idArray.Length < 1) return Ok(view);
  1642. int receiveId = idArray[0];
  1643. var receiveInfo = await _sqlSugar.Queryable<Pm_GoodsReceive>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveId);
  1644. if (receiveInfo == null) return Ok(view);
  1645. if (receiveInfo.IsReplace)
  1646. {
  1647. view.Msg += $"\r\n 需要归还的物资不向团组其他款项添加信息!";
  1648. return Ok(view);
  1649. }
  1650. var auditEnums = new List<GoodsAuditEnum>() { GoodsAuditEnum.OutConfirmed, GoodsAuditEnum.OutRejected };
  1651. if (!auditEnums.Contains(receiveInfo.AuditStatus)) return Ok(view);
  1652. var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveInfo.GroupId);
  1653. if (groupInfo == null) return Ok(view);
  1654. var basicData = await _sqlSugar.Queryable<Sys_SetData>().Where(x => x.IsDel == 0 && x.STid == 91).ToListAsync();
  1655. var goodsInfo = await _sqlSugar.Queryable<Pm_GoodsInfo>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveInfo.GoodsId);
  1656. if (goodsInfo == null) return Ok(view);
  1657. var basicInfo = basicData.FirstOrDefault(x => x.Name.Equals(goodsInfo.Name));
  1658. if (basicInfo == null) return Ok(view);
  1659. string priceName = basicInfo.Name;
  1660. _ = decimal.TryParse(basicInfo.Remark, out decimal price);
  1661. decimal total = price * receiveInfo.Quantity;
  1662. var requestData = new DecreasePaymentsOpDto()
  1663. {
  1664. Status = 1,
  1665. DiId = groupInfo.Id,
  1666. PriceName = priceName,
  1667. Price = price,
  1668. Quantity = receiveInfo.Quantity,
  1669. FeeTotal = total,
  1670. Currency = 836,
  1671. FilePath = "",
  1672. OrbitalPrivateTransfer = 0,
  1673. OTAOrderNo = "-",
  1674. OtherBankName = "-",
  1675. OtherSideName = "-",
  1676. OtherSideNo = "-",
  1677. PayDId = 105,
  1678. SupplierAddress = "-",
  1679. SupplierArea = 1,
  1680. SupplierContact = "-",
  1681. SupplierContactNumber = "-",
  1682. SupplierEmail = "-",
  1683. SupplierName = "-",
  1684. SupplierSocialAccount = "-",
  1685. SupplierTypeId = 0,
  1686. CreateUserId = receiveInfo.CreateUserId,
  1687. Remark = $"物资领用模块添加!!"
  1688. };
  1689. //其他费用
  1690. #region 调用方法
  1691. JsonView groupData = await _otherPaymentRep.OpDecreasePayments(requestData);
  1692. if (groupData.Code != 200)
  1693. {
  1694. view.Msg += groupData.Msg;
  1695. return Ok(view);
  1696. }
  1697. #region 应用推送
  1698. int diId = requestData.DiId, status = requestData.Status;
  1699. int otherId = Convert.ToInt32(groupData.Data.GetType().GetProperty("dataId").GetValue(groupData.Data, null) ?? 0);
  1700. int ccpId = Convert.ToInt32(groupData.Data.GetType().GetProperty("ccpId").GetValue(groupData.Data, null) ?? 0);
  1701. int sign = Convert.ToInt32(groupData.Data.GetType().GetProperty("sign").GetValue(groupData.Data, null) ?? 0);
  1702. //自动审核
  1703. var autoAdit = await _feeAuditRep.FeeAutomaticAudit(3, diId, otherId);
  1704. await AppNoticeLibrary.SendChatMsg_GroupStatus_ApplyFee(ccpId, sign, QiyeWeChatEnum.GuoJiaoLeaderChat);
  1705. //2024-10-21 新增LZ UID
  1706. var userIds = new List<int>() { 208 };
  1707. //var userIds = new List<int>() { 21 };
  1708. string title = $"系统通知";
  1709. string content = "";
  1710. var ccpInfo = await _sqlSugar.Queryable<Grp_CreditCardPayment>().Where(x => x.Id == ccpId).FirstAsync();
  1711. if (status == 1) content = $"[新增-其他款项({groupInfo?.TeamName ?? "-"})]一项费用:{ccpInfo.PayMoney * ccpInfo.DayRate:#0.00} CNY;";
  1712. else if (status == 2) content = $"[更新-其他款项({groupInfo?.TeamName ?? "-"})]一项费用:{ccpInfo.PayMoney * ccpInfo.DayRate:#0.00} CNY;";
  1713. await GeneralMethod.MessageIssueAndNotification(MessageTypeEnum.GroupBusinessOperations, title, content, userIds, diId);
  1714. //await APNsTools.iOS_PushNotifications1("051", $"其他款项费用审核", "", content);
  1715. #endregion
  1716. #endregion
  1717. #endregion
  1718. return Ok(view);
  1719. }
  1720. /// <summary>
  1721. /// 物资进销存
  1722. /// 领用 List(含批量)
  1723. /// </summary>
  1724. /// <returns></returns>
  1725. [HttpPost]
  1726. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1727. public async Task<IActionResult> GoodsReceiveBatchList(GoodsReceiveBatchListDto dto)
  1728. {
  1729. var currUserId = dto.CurrUserId;
  1730. var userInfo = await _sqlSugar.Queryable<Sys_Users>().FirstAsync(x => x.IsDel == 0 && x.Id == currUserId);
  1731. if (userInfo == null)
  1732. {
  1733. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1734. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1735. dto.CurrUserId = currUserInfo.UserId;
  1736. }
  1737. return Ok(await _goodsRep.GoodsReceiveBatchListAsync(dto));
  1738. }
  1739. /// <summary>
  1740. /// 物资进销存
  1741. /// 领用 OP 批量领用
  1742. /// </summary>
  1743. /// <returns></returns>
  1744. [HttpPost]
  1745. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1746. public async Task<IActionResult> GoodsReceiveBatchOp(GoodsReceiveBatchOpDto dto)
  1747. {
  1748. var currUserId = dto.CurrUserId;
  1749. var userInfo = await _sqlSugar.Queryable<Sys_Users>().FirstAsync(x => x.IsDel == 0 && x.Id == currUserId);
  1750. if (userInfo == null)
  1751. {
  1752. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1753. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1754. dto.CurrUserId = currUserInfo.UserId;
  1755. }
  1756. //参数验证
  1757. if (!dto.ReceiveDetails.Any()) Ok(JsonView(false, "请传入领用明细!"));
  1758. int index = 1;
  1759. foreach (var item in dto.ReceiveDetails)
  1760. {
  1761. //物品验证
  1762. if (item.GoodsId < 1) return Ok(JsonView(false, $"第{index}项物品无效!"));
  1763. //数量验证
  1764. if (item.Quantity <= 0.00M) return Ok(JsonView(false, $"第{index}项物品的数量无效!"));
  1765. index++;
  1766. }
  1767. var res = await _goodsRep.GoodsReceiveBatchOpAsync(dto);
  1768. //企业微信消息通知
  1769. if (res.Code == StatusCodes.Status200OK && dto.Id < 1)
  1770. {
  1771. var signProperty = res.Data?.GetType().GetProperty("sign");
  1772. int sign = signProperty != null && signProperty.GetValue(res.Data, null) is int value ? value : 0;
  1773. //物资领用通知
  1774. await AppNoticeLibrary.SendUserMsg_CompanyShare_ToHR(sign, dto.GroupId, dto.CurrUserId);
  1775. }
  1776. return Ok(res);
  1777. }
  1778. /// <summary>
  1779. /// 物资进销存
  1780. /// 领用 详情(含批量)
  1781. /// </summary>
  1782. /// <param name="id">id</param>
  1783. /// <returns></returns>
  1784. [HttpGet("{id}")]
  1785. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1786. public async Task<IActionResult> GoodsReceiveBatchInfo(int id)
  1787. {
  1788. return Ok(await _goodsRep.GoodsReceiveBatchInfoAsync(id));
  1789. }
  1790. /// <summary>
  1791. /// 物资进销存
  1792. /// 领用 审核 (含批量)
  1793. /// </summary>
  1794. /// <param name="dto"></param>
  1795. /// <returns></returns>
  1796. [HttpPost]
  1797. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1798. public async Task<IActionResult> GoodsReceiveAuditNew(GoodsReceiveAuditDTO dto)
  1799. {
  1800. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1801. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1802. var validator = new GoodsReceiveAuditDTOValidator();
  1803. var validatorRes = await validator.ValidateAsync(dto);
  1804. if (!validatorRes.IsValid)
  1805. {
  1806. var sb = new StringBuilder();
  1807. foreach (var error in validatorRes.Errors) sb.AppendLine(error.ErrorMessage);
  1808. return Ok(JsonView(false, sb.ToString()));
  1809. }
  1810. int[] idArray = dto.Label
  1811. .Split(',')
  1812. .Select(x =>
  1813. {
  1814. if (int.TryParse(x, out var id)) return id;
  1815. return id;
  1816. })
  1817. .ToArray();
  1818. if (idArray.Length < 1) return Ok(JsonView(false, "请传入有效的领用ID"));
  1819. int receiveId = idArray[0];
  1820. var receiveInfo = await _sqlSugar.Queryable<Pm_GoodsReceive>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveId);
  1821. if (receiveInfo == null) return Ok(JsonView(false, "请传入有效的领用ID"));
  1822. var view = new JsonView();
  1823. if (dto.AuditEnum == GoodsAuditEnum.Approved) //审核通过
  1824. {
  1825. view = await _goodsRep.GoodsReceiveApproveAsync(receiveId, currUserInfo.UserId);
  1826. }
  1827. else if (dto.AuditEnum == GoodsAuditEnum.UnApproved) //审核拒绝
  1828. {
  1829. view = await _goodsRep.GoodsReceiveRejectAsync(receiveId, currUserInfo.UserId);
  1830. }
  1831. if (view.Code != StatusCodes.Status200OK) return Ok(view);
  1832. receiveInfo = await _sqlSugar.Queryable<Pm_GoodsReceive>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveId);
  1833. var auditEnums = new List<GoodsAuditEnum>() { GoodsAuditEnum.OutConfirmed, GoodsAuditEnum.OutRejected };
  1834. if (receiveInfo.AuditStatus == GoodsAuditEnum.OutConfirming)
  1835. {
  1836. //物资领用通知
  1837. await AppNoticeLibrary.SendUserMsg_CompanyShare_ToHR(receiveId, receiveInfo.GroupId, receiveInfo.CreateUserId);
  1838. }
  1839. #region 出库成功 && 不需要归还的物资(IsReplace == false) && 团组相关物资 向团组其他款项添加信息
  1840. //if (!auditEnums.Contains(receiveInfo.AuditStatus)) return Ok(view);
  1841. if (receiveInfo.IsReplace)
  1842. {
  1843. view.Msg += $"\r\n 需要归还的物资不向团组其他款项添加信息!";
  1844. return Ok(view);
  1845. }
  1846. var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveInfo.GroupId);
  1847. if (groupInfo == null) return Ok(view);
  1848. var basicData = await _sqlSugar.Queryable<Sys_SetData>().Where(x => x.IsDel == 0 && x.STid == 91).ToListAsync();
  1849. var receiveDetails = await _sqlSugar.Queryable<Pm_GoodsReceiveDetails>()
  1850. .Where(x => x.IsDel == 0 && x.GoodsReceiveId == receiveInfo.Id)
  1851. .ToListAsync();
  1852. if (!receiveDetails.Any()) return Ok(view);
  1853. var goodsIds = receiveDetails.Select(y => y.GoodsId).ToList();
  1854. var goodsInfos = await _sqlSugar.Queryable<Pm_GoodsInfo>()
  1855. .Where(x => x.IsDel == 0 && goodsIds.Contains(x.Id))
  1856. .ToListAsync();
  1857. if (!goodsInfos.Any()) return Ok(view);
  1858. string priceName = string.Join("、", goodsInfos.Select(x => x.Name).ToList());
  1859. //物品价格 按照物品批次单价计算
  1860. var goodsPriceDetails = new StringBuilder();
  1861. decimal price = 0.00M; //物品累计价格
  1862. var goodsStorages = await _sqlSugar.Queryable<Pm_GoodsStorage>().Where(x => x.IsDel == 0 && goodsIds.Contains(x.GoodsId)).ToListAsync();
  1863. foreach (var item in receiveDetails)
  1864. {
  1865. List<GoodsReceiveLinkStorageView> storageFlows = new();
  1866. if (!string.IsNullOrEmpty(item.GoodsStorageInfo))
  1867. {
  1868. storageFlows = JsonConvert.DeserializeObject<List<GoodsReceiveLinkStorageView>>(item.GoodsStorageInfo);
  1869. }
  1870. if (storageFlows.Any())
  1871. {
  1872. storageFlows.ForEach(storage =>
  1873. {
  1874. price += goodsStorages.FirstOrDefault(x => x.Id == storage.StorageId)?.UnitPrice ?? 0m * storage.Quantity;
  1875. });
  1876. }
  1877. }
  1878. decimal quantity = 1m;
  1879. var total = price * quantity;
  1880. string remark = $"物资领用模块添加!需核对领用物品价格!!!";
  1881. if (!string.IsNullOrEmpty(receiveInfo.Remark)) remark = $"{receiveInfo.Remark}\n物资领用模块添加!需核对领用物品价格!!!";
  1882. var requestData = new DecreasePaymentsOpDto()
  1883. {
  1884. Status = 1,
  1885. DiId = groupInfo.Id,
  1886. PriceName = priceName,
  1887. Price = price,
  1888. Quantity = quantity,
  1889. FeeTotal = total,
  1890. Currency = 836,
  1891. FilePath = "",
  1892. OrbitalPrivateTransfer = 0,
  1893. OTAOrderNo = "-",
  1894. OtherBankName = "-",
  1895. OtherSideName = "-",
  1896. OtherSideNo = "-",
  1897. PayDId = 105,
  1898. SupplierAddress = "-",
  1899. SupplierArea = 1,
  1900. SupplierContact = "-",
  1901. SupplierContactNumber = "-",
  1902. SupplierEmail = "-",
  1903. SupplierName = "-",
  1904. SupplierSocialAccount = "-",
  1905. SupplierTypeId = 0,
  1906. CreateUserId = receiveInfo.CreateUserId,
  1907. Remark = remark
  1908. };
  1909. //1 2590 √
  1910. //2 2737 √ √
  1911. //3 2760 √ √
  1912. //4 2761 √ √
  1913. //5 2788 √ √
  1914. //6 2796 √ √
  1915. //7 2809 √ √
  1916. //8 2833 √ √
  1917. //9 2834 √ √
  1918. //10 2839 √ √
  1919. //11 2846 √ √
  1920. //其他费用
  1921. #region 调用方法
  1922. JsonView groupData = await _otherPaymentRep.OpDecreasePayments(requestData);
  1923. if (groupData.Code != 200)
  1924. {
  1925. view.Msg += groupData.Msg;
  1926. return Ok(view);
  1927. }
  1928. #region 应用推送
  1929. int diId = requestData.DiId, status = requestData.Status;
  1930. int otherId = Convert.ToInt32(groupData.Data.GetType().GetProperty("dataId").GetValue(groupData.Data, null) ?? 0);
  1931. int ccpId = Convert.ToInt32(groupData.Data.GetType().GetProperty("ccpId").GetValue(groupData.Data, null) ?? 0);
  1932. int sign = Convert.ToInt32(groupData.Data.GetType().GetProperty("sign").GetValue(groupData.Data, null) ?? 0);
  1933. //自动审核
  1934. //var autoAdit = await _feeAuditRep.FeeAutomaticAudit(3, diId, otherId);
  1935. await AppNoticeLibrary.SendChatMsg_GroupStatus_ApplyFee(ccpId, sign, QiyeWeChatEnum.GuoJiaoLeaderChat);
  1936. //2024-10-21 新增LZ UID
  1937. var userIds = new List<int>() { 208 };
  1938. //var userIds = new List<int>() { 21 };
  1939. string title = $"系统通知";
  1940. string content = "";
  1941. var ccpInfo = await _sqlSugar.Queryable<Grp_CreditCardPayment>().Where(x => x.Id == ccpId).FirstAsync();
  1942. if (status == 1) content = $"[新增-其他款项({groupInfo?.TeamName ?? "-"})]一项费用:{(ccpInfo.PayMoney * ccpInfo.DayRate).ToString("#0.00")} CNY;";
  1943. else if (status == 2) content = $"[更新-其他款项({groupInfo?.TeamName ?? "-"})]一项费用:{(ccpInfo.PayMoney * ccpInfo.DayRate).ToString("#0.00")} CNY;";
  1944. await GeneralMethod.MessageIssueAndNotification(MessageTypeEnum.GroupBusinessOperations, title, content, userIds, diId);
  1945. //await APNsTools.iOS_PushNotifications1("051", $"其他款项费用审核", "", content);
  1946. #endregion
  1947. #endregion
  1948. #endregion
  1949. return Ok(view);
  1950. }
  1951. /// <summary>
  1952. /// 物资进销存
  1953. /// 领用 Del
  1954. /// </summary>
  1955. /// <returns></returns>
  1956. [HttpDelete("{id}")]
  1957. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  1958. public async Task<IActionResult> GoodsReceiveDel(int id)
  1959. {
  1960. var currUserInfo = JwtHelper.SerializeJwt(HttpContext.Request.Headers.Authorization);
  1961. if (currUserInfo == null) return Ok(JsonView(false, "请传入token!"));
  1962. if (id < 1) return Ok(JsonView(false, MsgTips.Id));
  1963. return Ok(await _goodsRep.GoodsReceiveDel(id, currUserInfo.UserId));
  1964. }
  1965. #endregion
  1966. #region 员工绩效
  1967. /// <summary>
  1968. /// 员工绩效组成结构获取
  1969. /// </summary>
  1970. /// <param name="userid"></param>
  1971. /// <param name="date"></param>
  1972. /// <param name="loginUser"></param>
  1973. /// <returns></returns>
  1974. [HttpGet]
  1975. public IActionResult GetPerformanceList(int userid, string date, int loginUser)
  1976. {
  1977. var jw = JsonView(false);
  1978. if (!DateTime.TryParse(date, out DateTime date_Dt))
  1979. {
  1980. jw.Msg = "日期格式有误!";
  1981. return Ok(jw);
  1982. }
  1983. string sql_CTE = $@"-- 定义递归查询的CTE
  1984. WITH PerAssessmentSettingsCTE AS (
  1985. -- 选择指定ID的记录作为起点
  1986. SELECT
  1987. pac.Id,
  1988. pac.Name,
  1989. pac.AssessmentProportion,
  1990. pac.AssessmentStandard,
  1991. pac.ParentId,
  1992. 1 AS Depth
  1993. FROM
  1994. Per_AssessmentSetting pac
  1995. INNER JOIN
  1996. Per_AssessmentContentSetting pacs
  1997. ON
  1998. pac.id = pacs.assessmentSettingid AND pacs.isdel = 0
  1999. WHERE
  2000. pacs.userid = {userid}
  2001. AND
  2002. pac.isdel = 0
  2003. UNION ALL
  2004. -- 递归部分,选择所有子级记录
  2005. SELECT
  2006. ps.Id,
  2007. ps.Name,
  2008. ps.AssessmentProportion,
  2009. ps.AssessmentStandard,
  2010. ps.ParentId,
  2011. CAST(caste.Depth as int) + 1
  2012. FROM
  2013. Per_AssessmentSetting ps
  2014. INNER JOIN PerAssessmentSettingsCTE caste ON ps.Id = caste.ParentId)
  2015. -- 从CTE中选择最终结果
  2016. SELECT * FROM PerAssessmentSettingsCTE
  2017. OPTION (MAXRECURSION 0); -- 允许无限递归 ";
  2018. var result_CTE = _sqlSugar.Ado.SqlQuery<Per_AssessmentSetting>(sql_CTE);
  2019. var ids = result_CTE.Select(x => x.Id);
  2020. var List = _sqlSugar.Queryable<Per_AssessmentSetting>()
  2021. .LeftJoin<Per_AssessmentContentSetting>((a, b) => a.Id == b.AssessmentSettingId && b.IsDel == 0 && b.UserId == userid)
  2022. .LeftJoin<Per_AssessmentScore>((a, b, c) => b.Id == c.AssessmentContentSettingId && c.IsDel == 0 && c.YearMonth.Year == date_Dt.Year && c.YearMonth.Month == date_Dt.Month)
  2023. .Where((a, b) => a.IsDel == 0 && ids.Contains(a.Id))
  2024. .Select((a, b, c) => new TreeNode
  2025. {
  2026. Id = a.Id,
  2027. ContentId = b.Id,
  2028. Name = a.Name,
  2029. ParentId = a.ParentId,
  2030. AssessmentProportion = a.AssessmentProportion, //占比
  2031. AssessmentStandard = a.AssessmentStandard, //描述
  2032. AssessmentProportionChi = b.AssessmentProportionChi, //占比
  2033. UserId = b.UserId,
  2034. JobId = b.JobId,
  2035. AssessmentSettingId = b.AssessmentSettingId,
  2036. Fixed = b.Fixed,
  2037. TargetValue = b.TargetValue,
  2038. AssessmentProportion_Percentage = a.AssessmentProportion * 100,
  2039. HigherUpAssessment = c.HigherUpAssessment,
  2040. HigherUpConfig = c.HigherUpConfig,
  2041. HigherUpUserId = c.HigherUpUserId,
  2042. Score = c.Score,
  2043. ScoreTotal = c.ScoreTotal,
  2044. SelfAssessment = c.SelfAssessment,
  2045. Status = c.Status,
  2046. YearMonth = c.YearMonth,
  2047. })
  2048. .ToList();
  2049. var rootNodeList = List.Where(x => x.ParentId == 0);
  2050. var rootResult = rootNodeList.Select(x => BuildSubTree(x, List));
  2051. jw.Data = new
  2052. {
  2053. Root = rootResult,
  2054. IsLeader = IsLeader(userid, loginUser)
  2055. };
  2056. jw.Code = 200;
  2057. jw.Msg = "成功";
  2058. return Ok(jw);
  2059. }
  2060. private static TreeNode BuildSubTree(TreeNode parent, List<TreeNode> nodes)
  2061. {
  2062. var children = nodes
  2063. .Where(n => n.ParentId == parent.Id)
  2064. .Select(n => BuildSubTree(n, nodes))
  2065. .ToList();
  2066. parent.Children = children;
  2067. return parent;
  2068. }
  2069. /// <summary>
  2070. /// 绩效项增改
  2071. /// </summary>
  2072. /// <param name="dto"></param>
  2073. /// <returns></returns>
  2074. [HttpPost]
  2075. public async Task<IActionResult> AssessmentSettingOperationAsync(PerAssessmentSettingOperationDto dto)
  2076. {
  2077. var jw = JsonView(false);
  2078. if (!ModelState.IsValid)
  2079. {
  2080. jw.Msg = "参数错误";
  2081. jw.Data = ModelState;
  2082. return Ok(jw);
  2083. }
  2084. try
  2085. {
  2086. if (dto.ParentId != 0)
  2087. {
  2088. var parent = await _sqlSugar.Queryable<Per_AssessmentSetting>().FirstAsync(x => x.IsDel == 0 && x.Id == dto.ParentId);
  2089. if (parent == null)
  2090. {
  2091. jw.Msg = "父级节点不存在";
  2092. return Ok(jw);
  2093. }
  2094. }
  2095. var entity = new Per_AssessmentSetting
  2096. {
  2097. Name = dto.Name,
  2098. AssessmentProportion = dto.AssessmentProportion,
  2099. AssessmentStandard = dto.AssessmentStandard,
  2100. ParentId = dto.ParentId,
  2101. Id = dto.Id,
  2102. Remark = dto.Remark,
  2103. };
  2104. jw.Code = 200;
  2105. if (dto.Id == 0)
  2106. {
  2107. entity.CreateUserId = dto.CreateId;
  2108. entity.CreateTime = DateTime.Now;
  2109. await _sqlSugar.Insertable(entity).ExecuteCommandAsync();
  2110. jw.Msg = "添加成功!";
  2111. }
  2112. else
  2113. {
  2114. await _sqlSugar.Updateable(entity).ExecuteCommandAsync();
  2115. jw.Msg = "修改成功!";
  2116. }
  2117. }
  2118. catch (Exception ex)
  2119. {
  2120. jw.Msg = "Api error " + ex.Message;
  2121. jw.Code = 400;
  2122. }
  2123. return Ok(jw);
  2124. }
  2125. /// <summary>
  2126. /// 绩效内容增改
  2127. /// </summary>
  2128. /// <param name="dto"></param>
  2129. /// <returns></returns>
  2130. [HttpPost]
  2131. public IActionResult AssessmentSettingOperationContentAsync(AssessmentSettingOperationContenDto dto)
  2132. {
  2133. var jw = JsonView(false, "", "");
  2134. var entity = new Per_AssessmentContentSetting
  2135. {
  2136. AssessmentProportionChi = dto.AssessmentProportionChi,
  2137. AssessmentSettingId = dto.AssessmentSettingId,
  2138. CreateTime = DateTime.Now,
  2139. CreateUserId = dto.CreateUserId,
  2140. Fixed = dto.Fixed,
  2141. Id = dto.Id,
  2142. JobId = dto.JobId,
  2143. TargetValue = dto.TargetValue,
  2144. UserId = dto.UserId,
  2145. Remark = dto.Remark,
  2146. };
  2147. var entity_Fk = _sqlSugar.Queryable<Per_AssessmentSetting>().First(x => x.Id == dto.AssessmentSettingId && x.IsDel == 0);
  2148. if (entity_Fk == null)
  2149. {
  2150. jw.Msg = "考核设置不存在";
  2151. return Ok(jw);
  2152. }
  2153. try
  2154. {
  2155. if (dto.Id == 0)
  2156. {
  2157. //add
  2158. var insertCount = _sqlSugar.Insertable(entity).ExecuteCommand();
  2159. jw.Msg = "添加成功!";
  2160. }
  2161. else
  2162. {
  2163. //update
  2164. var updateCount = _sqlSugar.Updateable(entity).ExecuteCommand();
  2165. jw.Msg = "修改成功!";
  2166. }
  2167. jw.Code = 200;
  2168. }
  2169. catch (Exception ex)
  2170. {
  2171. jw.Msg = "Api error " + ex.Message;
  2172. jw.Code = 400;
  2173. }
  2174. return Ok(jw);
  2175. }
  2176. /// <summary>
  2177. /// 绩效项查询
  2178. /// </summary>
  2179. /// <param name="dto"></param>
  2180. /// <returns></returns>
  2181. [HttpPost]
  2182. public async Task<IActionResult> QueryAssessmentSettingListOffsetAsync(QueryAssessmentSettingListOffsetAsyncDto dto)
  2183. {
  2184. var jw = JsonView(false);
  2185. RefAsync<int> total = 0;//REF和OUT不支持异步,想要真的异步这是最优解
  2186. var entities = await _sqlSugar.Queryable<Per_AssessmentSetting>()
  2187. .Where(x => x.IsDel == 0)
  2188. .WhereIF(!string.IsNullOrEmpty(dto.SearchValue), e => e.Name.Contains(dto.SearchValue))
  2189. .ToPageListAsync(dto.pageIndex, dto.pageSize, total);
  2190. var DtoResult = entities.Select(e => new
  2191. {
  2192. e.Id,
  2193. e.Name,
  2194. e.AssessmentProportion,
  2195. e.AssessmentStandard,
  2196. e.ParentId,
  2197. e.Remark
  2198. }).ToList();
  2199. jw.Data = new
  2200. {
  2201. total = total.Value,
  2202. dto.pageIndex,
  2203. dto.pageSize,
  2204. List = DtoResult
  2205. };
  2206. jw.Code = 200;
  2207. jw.Msg = "查询成功!";
  2208. return Ok(jw);
  2209. }
  2210. /// <summary>
  2211. /// 绩效分数保存
  2212. /// </summary>
  2213. /// <param name="dto"></param>
  2214. /// <returns></returns>
  2215. [HttpPost]
  2216. public IActionResult SaveUserAssessmen(SaveUserAssessmenDto dto)
  2217. {
  2218. var jw = JsonView(false, "请传入正确的数据!");
  2219. if (dto.Data.Any())
  2220. {
  2221. if (!DateTime.TryParse(dto.AssessmenData, out DateTime yearMonth_Dt))
  2222. {
  2223. jw.Data = "月份参数有误!";
  2224. return Ok(jw);
  2225. }
  2226. try
  2227. {
  2228. var ids = dto.Data.Select(x => x.AssessmentContentSettingId).ToList();
  2229. var QueryContent_DB = _sqlSugar.Queryable<Per_AssessmentContentSetting>()
  2230. .InnerJoin<Per_AssessmentSetting>((a, b) => b.IsDel == 0 && a.AssessmentSettingId == b.Id)
  2231. .Where((a, b) => a.IsDel == 0 && ids.Contains(a.Id))
  2232. .Select((a, b) => new
  2233. {
  2234. ContentSettingId = a.Id,
  2235. SettingId = b.Id,
  2236. a.TargetValue,
  2237. b.AssessmentStandard,
  2238. MergeStr = $"【项名称:{b.Name}/目标值:{a.TargetValue}/评估标准:{b.AssessmentStandard}】",
  2239. a.AssessmentProportionChi,
  2240. b.Name
  2241. })
  2242. .ToList()
  2243. .ToDictionary(x => x.ContentSettingId);
  2244. //删除上级未确认数据
  2245. var expressionWhere = Expressionable
  2246. .Create<Per_AssessmentScore>()
  2247. .And(x => x.YearMonth.Year == yearMonth_Dt.Year && x.YearMonth.Month == yearMonth_Dt.Month)
  2248. .And(x => ids.Contains(x.AssessmentContentSettingId))
  2249. .And(x => x.HigherUpConfig == 0)
  2250. .ToExpression();
  2251. if (IsLeader(dto.AssessmenUserId, dto.CreateUserId))
  2252. {
  2253. expressionWhere = Expressionable
  2254. .Create<Per_AssessmentScore>()
  2255. .And(x => x.YearMonth.Year == yearMonth_Dt.Year && x.YearMonth.Month == yearMonth_Dt.Month)
  2256. .And(x => ids.Contains(x.AssessmentContentSettingId))
  2257. .ToExpression();
  2258. }
  2259. _sqlSugar.BeginTran();
  2260. _sqlSugar.Updateable<Per_AssessmentScore>()
  2261. .Where(expressionWhere)
  2262. .SetColumns(x => new Per_AssessmentScore { IsDel = 1, DeleteUserId = dto.CreateUserId, DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") })
  2263. .ExecuteCommand();
  2264. var entityList = new List<Per_AssessmentScore>();
  2265. foreach (var x in dto.Data)
  2266. {
  2267. if (x.HigherUpAssessment > 100 || x.SelfAssessment > 100)
  2268. {
  2269. jw.Msg = QueryContent_DB[x.AssessmentContentSettingId].Name + "项分数不能超过100。";
  2270. return Ok(jw);
  2271. }
  2272. Per_AssessmentScore item = new Per_AssessmentScore
  2273. {
  2274. AssessmentContentSettingId = x.AssessmentContentSettingId,
  2275. CreateTime = DateTime.Now,
  2276. CreateUserId = dto.CreateUserId,
  2277. Details = QueryContent_DB[x.AssessmentContentSettingId].MergeStr,
  2278. HigherUpAssessment = x.HigherUpAssessment,
  2279. HigherUpUserId = x.LeadersId,
  2280. HigherUpConfig = x.HigherUpAssessment > 0 ? 1 : 0,
  2281. SelfAssessment = x.SelfAssessment,
  2282. IsDel = 0,
  2283. Status = x.Status,
  2284. YearMonth = yearMonth_Dt,
  2285. Score = x.SelfAssessment * 0.3M + x.HigherUpAssessment * 0.7M,
  2286. ScoreTotal = (x.SelfAssessment * 0.3M + x.HigherUpAssessment * 0.7M) * QueryContent_DB[x.AssessmentContentSettingId].AssessmentProportionChi,
  2287. };
  2288. entityList.Add(item);
  2289. }
  2290. var influenceRow = _sqlSugar.Insertable(entityList).ExecuteCommand();
  2291. _sqlSugar.CommitTran();
  2292. jw.Code = 200;
  2293. jw.Msg = "保存成功!";
  2294. jw.Data = $"{influenceRow}条数据操作成功!";
  2295. }
  2296. catch (Exception ex)
  2297. {
  2298. _sqlSugar.RollbackTran();
  2299. jw.Code = 500;
  2300. jw.Data = $"API ERROR ({ex.Message})";
  2301. }
  2302. }
  2303. return Ok(jw);
  2304. }
  2305. /// <summary>
  2306. /// 绩效项删除
  2307. /// </summary>
  2308. /// <param name="dto"></param>
  2309. /// <returns></returns>
  2310. [HttpPost]
  2311. public IActionResult DeleteAssessmentSetting(DeleteAssessmentSettingDto dto)
  2312. {
  2313. var jw = JsonView(false, "删除失败!");
  2314. if (!dto.IdArr.Any())
  2315. {
  2316. jw.Msg = "id不能为空!";
  2317. return Ok(jw);
  2318. }
  2319. var rowCount = _sqlSugar.Updateable<Per_AssessmentSetting>()
  2320. .Where(x => dto.IdArr.Contains(x.Id) && x.IsDel == 0)
  2321. .SetColumns(x => new Per_AssessmentSetting
  2322. {
  2323. IsDel = 1,
  2324. DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
  2325. DeleteUserId = dto.UserId,
  2326. })
  2327. .ExecuteCommand();
  2328. jw.Code = 200;
  2329. jw.Msg = $"修改成功!,修改数量{rowCount}";
  2330. return Ok(jw);
  2331. }
  2332. /// <summary>
  2333. /// 绩效内容删除
  2334. /// </summary>
  2335. /// <param name="dto"></param>
  2336. /// <returns></returns>
  2337. [HttpPost]
  2338. public IActionResult DeleteAssessmentContentSetting(DeleteAssessmentSettingDto dto)
  2339. {
  2340. var jw = JsonView(false, "删除失败!");
  2341. if (!dto.IdArr.Any())
  2342. {
  2343. jw.Msg = "id不能为空!";
  2344. return Ok(jw);
  2345. }
  2346. var rowCount = _sqlSugar.Updateable<Per_AssessmentContentSetting>()
  2347. .Where(x => dto.IdArr.Contains(x.Id) && x.IsDel == 0)
  2348. .SetColumns(x => new Per_AssessmentContentSetting
  2349. {
  2350. IsDel = 1,
  2351. DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
  2352. DeleteUserId = dto.UserId,
  2353. })
  2354. .ExecuteCommand();
  2355. jw.Code = 200;
  2356. jw.Msg = $"修改成功!,修改数量{rowCount}";
  2357. return Ok(jw);
  2358. }
  2359. private bool IsLeader(int userId, int higherUserId)
  2360. {
  2361. Dictionary<int, List<int>> keyValues = new Dictionary<int, List<int>>()
  2362. {
  2363. { 5 , new List<int>{ 258 , 235, 234, 233, 208 } }
  2364. };
  2365. if (keyValues.ContainsKey(higherUserId))
  2366. {
  2367. return keyValues[higherUserId].Contains(userId);
  2368. }
  2369. return false;
  2370. }
  2371. #endregion
  2372. #region 企微Api测试
  2373. [HttpPost]
  2374. public async Task<IActionResult> GetCheckin_MonthDataAsync(DateTime start, DateTime end)
  2375. {
  2376. var jw = JsonView(false);
  2377. jw.Data = await _qiYeWeChatApiService.GetCheckin_MonthDataAsync1(start, end);
  2378. return Ok(jw);
  2379. }
  2380. [HttpPost]
  2381. public async Task<IActionResult> QueryAssessmentByUser(QueryAssessmentByUser Dto)
  2382. {
  2383. var jw = JsonView(false);
  2384. var user_entity = _sqlSugar.Queryable<Sys_Users>()
  2385. .First(e => e.Id == Dto.UserId && e.IsDel == 0);
  2386. var start_Bool = DateTime.TryParse(Dto.Start, out DateTime start);
  2387. var end_Bool = DateTime.TryParse(Dto.End, out DateTime end);
  2388. if (!start_Bool || !end_Bool)
  2389. {
  2390. jw.Msg = "时间格式不正确!";
  2391. return Ok(jw);
  2392. }
  2393. jw.Msg = "用户企业微信Id未绑定!";
  2394. if (user_entity != null && !string.IsNullOrEmpty(user_entity.QiyeChatUserId))
  2395. {
  2396. var data = await _qiYeWeChatApiService.QueryAssessmentByUser(start, end, new List<string> { user_entity.QiyeChatUserId });
  2397. jw.Data = data;
  2398. jw.Msg = "查询成功!";
  2399. jw.Code = 200;
  2400. }
  2401. return Ok(jw);
  2402. }
  2403. //[HttpPost]
  2404. //public IActionResult TempTest(QueryAssessmentSettingListOffsetAsyncDto Dto)
  2405. //{
  2406. // var jw = JsonView(false);
  2407. // return Ok(jw);
  2408. //}
  2409. #endregion
  2410. #region Ai绩效分析
  2411. public class AiResponse
  2412. {
  2413. /// <summary>
  2414. /// 业务分析答案
  2415. /// </summary>
  2416. public string Answer { get; set; }
  2417. /// <summary>
  2418. /// 考勤分析答案
  2419. /// </summary>
  2420. public string KaoqinAnswer { get; set; }
  2421. }
  2422. /// <summary>
  2423. /// 市场部员工分析
  2424. /// </summary>
  2425. /// <param name="userId"></param>
  2426. /// <param name="start"></param>
  2427. /// <param name="end"></param>
  2428. /// <param name="createUserId"></param>
  2429. /// <returns></returns>
  2430. [HttpGet]
  2431. public async Task<IActionResult> AiPerformanceAnalysis_JobMarketingAsync
  2432. (int userId, DateTime start, DateTime end, int createUserId)
  2433. {
  2434. var jw = JsonView(false);
  2435. var user_entity = _sqlSugar.Queryable<Sys_Users>()
  2436. .First(e => e.Id == userId && e.IsDel == 0);
  2437. if (user_entity == null)
  2438. {
  2439. jw.Msg = "用户不存在!";
  2440. return Ok(jw);
  2441. }
  2442. List<string> useridlist = new List<string> { user_entity.QiyeChatUserId };
  2443. //获取企微外出打卡信息
  2444. var checkin_data = await _qiYeWeChatApiService.GetCheckinDataAsync(useridlist, 2, start, end);
  2445. if (checkin_data.errcode != 0)
  2446. {
  2447. jw.Msg = "获取企微外出打卡信息失败!" + checkin_data.errmsg;
  2448. return Ok(jw);
  2449. }
  2450. //获取企微外出申请信息
  2451. var approval_data = await _qiYeWeChatApiService.GetApprovalInfoUpdateAsync(start, end, newCursor: "", size: 100, filters: new List<IQiYeWeChatApiService.FilterCondition>(){
  2452. new
  2453. IQiYeWeChatApiService.FilterCondition{
  2454. key = "creator",
  2455. value = user_entity.QiyeChatUserId
  2456. },
  2457. new IQiYeWeChatApiService.FilterCondition{
  2458. key="record_type",
  2459. value="4"
  2460. }
  2461. });
  2462. if (approval_data.errcode != 0)
  2463. {
  2464. jw.Msg = "获取企微外出申请信息失败!" + approval_data.errmsg;
  2465. return Ok(jw);
  2466. }
  2467. List<ApprovalDetailView> detailViews = new List<ApprovalDetailView>();
  2468. foreach (var item in approval_data.sp_no_list)
  2469. {
  2470. var approval_detail = await _qiYeWeChatApiService.GetApprovalDetailAsync(item);
  2471. if (approval_detail.errcode != 0)
  2472. {
  2473. jw.Msg = "获取企微外出申请详情失败!" + approval_detail.errmsg;
  2474. return Ok(jw);
  2475. }
  2476. detailViews.Add(approval_detail);
  2477. }
  2478. string question = $"请根据以下数据分析用户绩效:";
  2479. question += $"外出打卡信息:{JsonConvert.SerializeObject(checkin_data.checkindata)}";
  2480. if (approval_data.sp_no_list.Count > 0)
  2481. {
  2482. question += $"外出申请信息:{JsonConvert.SerializeObject(detailViews)}";
  2483. }
  2484. question += $"请根据我提供的{user_entity.CnName}数据信息(提供的数据已经是一个月的数据),按照一个月拜访20个客户的标准判断是否合格,并给出分析报告。";
  2485. question += @"输出内容格式参考这个我下面提供的模板 “根据您提供的陈小龙2025年9月外出打卡数据,现进行绩效分析如下:\n\n### 一、基础数据统计\n**总打卡次数**:16次 \n**覆盖日期**:9月2日-9月29日(共28天) \n**有效工作日**:22天(扣除周末)\n\n### 二、客户拜访量分析\n1. **实际拜访客户数**:9个\n - 重庆商务职业学院(2次)\n - 重庆市南岸区商务委员会(2次)\n - 重庆市城投金卡信息产业(集团)股份有限公司(2次)\n - 玉马路\n - 重庆市南岸区人民政府外事办公室\n - 重庆经济技术开发区\n - 中共南岸区委党校\n - 重庆市南岸区社会主义学院教学楼\n - 重庆建筑工程职业学院(东站校区)\n - 中德职业教育西部中职示范基地\n - 重庆市璧山区人民政府\n\n2. **与标准对比**:\n - 目标拜访量:20个客户/月\n - 完成率:45%(9/20)\n - **结论:未达到合格标准**\n\n### 三、工作模式分析\n**优势**:\n1. 区域集中度较高:主要活跃在南岸区茶园片区(11次打卡)\n2. 重要客户重复拜访:3个重点客户有2次以上跟进记录\n3. 单日效率突出:9月26日完成4个点位拜访\n\n**待改进点**:\n1. 出勤稳定性不足:28天中仅16天有外出记录\n2. 单日拜访密度低:日均拜访0.57个客户\n3. 时间利用不均衡:多个工作日上午无外出记录\n\n### 四、改进建议\n1. **提升拜访频率**:\n - 建议将日均拜访量提升至1-1.5个客户\n - 合理安排行程,减少单点停留时间\n\n2. **优化区域规划**:\n - 继续保持南岸区的集中开发\n - 沙坪坝区(大学城)客户可集中安排单日拜访\n\n3. **加强过程管理**:\n - 建立每日拜访计划\n - 重要客户保持每月2-3次跟进频率\n\n### 五、综合评级:不合格\n按现行标准(20个客户/月),完成率仅45%。建议结合以下因素综合评估:\n- 客户质量(政府机构/职业院校占比高)\n- 单次拜访实效(需结合业务成果)\n- 区域开发难度\n\n是否需要调整考核标准或提供专项支持,建议直属领导进行面谈沟通。\n\n---\n**数据说明**:本分析仅基于打卡位置去重计算,实际客户数量可能需要结合业务系统数据进一步确认。”";
  2486. var settingTemp = await _sqlSugar.Queryable<Sys_SetData>().FirstAsync(x => x.Id == 1461 && x.IsDel == 0);
  2487. if (settingTemp != null && !string.IsNullOrEmpty(settingTemp.Remark))
  2488. {
  2489. var stringFormatResp = await GeneralMethod.StringFormatAsync(new StringFormatDto
  2490. {
  2491. FormatTemplate = settingTemp.Remark,
  2492. Parameters = new List<string> {
  2493. JsonConvert.SerializeObject(checkin_data.checkindata),
  2494. JsonConvert.SerializeObject(detailViews),
  2495. user_entity.CnName
  2496. }
  2497. });
  2498. if (stringFormatResp.Success)
  2499. {
  2500. question = stringFormatResp.FormattedResult;
  2501. }
  2502. }
  2503. try
  2504. {
  2505. var resp = await _deepSeekService.ChatAsync(question);
  2506. if (!resp.Success)
  2507. {
  2508. jw.Msg = "Ai分析用户绩效失败!" + resp.Message;
  2509. return Ok(jw);
  2510. }
  2511. //获取企微上下班考勤打卡信息
  2512. checkin_data = await _qiYeWeChatApiService.GetCheckinDataAsync(useridlist, 1, start, end);
  2513. if (checkin_data.errcode != 0)
  2514. {
  2515. jw.Msg = "获取企微上下班考勤打卡信息失败!" + checkin_data.errmsg;
  2516. return Ok(jw);
  2517. }
  2518. question = $"{JsonConvert.SerializeObject(checkin_data.checkindata)},请根据我提供的{user_entity.CnName}数据信息(提供的数据已经是一个月的数据),分析该员工日常考勤情况 ,工作日每天需要打卡俩次分别是早上9点和下午18点,"
  2519. + "输出内容标题不要带有表情或者符号。";
  2520. settingTemp = await _sqlSugar.Queryable<Sys_SetData>().FirstAsync(x => x.Id == 1462 && x.IsDel == 0);
  2521. if (settingTemp != null && !string.IsNullOrEmpty(settingTemp.Remark))
  2522. {
  2523. var stringFormatResp = await GeneralMethod.StringFormatAsync(new StringFormatDto
  2524. {
  2525. FormatTemplate = settingTemp.Remark,
  2526. Parameters = new List<string> {
  2527. JsonConvert.SerializeObject(checkin_data.checkindata),
  2528. user_entity.CnName
  2529. }
  2530. });
  2531. if (stringFormatResp.Success)
  2532. {
  2533. question = stringFormatResp.FormattedResult;
  2534. }
  2535. }
  2536. var kaoqinResp = await _deepSeekService.ChatAsync(question);
  2537. if (!resp.Success)
  2538. {
  2539. jw.Msg = "Ai分析用户考勤失败!" + resp.Message;
  2540. return Ok(jw);
  2541. }
  2542. jw.Data = new AiResponse()
  2543. {
  2544. Answer = resp.Answer,
  2545. KaoqinAnswer = kaoqinResp.Answer
  2546. };
  2547. //保存至数据库中
  2548. Pm_PerformanceAnalysis insertData = new Pm_PerformanceAnalysis
  2549. {
  2550. CreateTime = DateTime.Now,
  2551. CreateUserId = createUserId,
  2552. IsDel = 0,
  2553. Year = start.Year,
  2554. Month = start.Month,
  2555. JsonResult = JsonConvert.SerializeObject(jw.Data),
  2556. UserId = userId,
  2557. };
  2558. await _sqlSugar.Insertable(insertData).ExecuteCommandAsync();
  2559. }
  2560. catch (Exception ex)
  2561. {
  2562. jw.Msg = "Ai分析用户绩效失败!" + ex.Message;
  2563. return Ok(jw);
  2564. }
  2565. jw.Code = 200;
  2566. jw.Msg = "查询成功!";
  2567. return Ok(jw);
  2568. }
  2569. /// <summary>
  2570. /// AI员工绩效分析
  2571. /// </summary>
  2572. /// <param name="userId">用户ID</param>
  2573. /// <param name="start">开始时间</param>
  2574. /// <param name="end">结束时间</param>
  2575. /// <param name="createUserId">创建人ID</param>
  2576. /// <param name="processType">流程类型:3=机票,4=酒店</param>
  2577. /// <returns></returns>
  2578. private async Task<IActionResult> AiPerformanceAnalysisAsync(int userId, DateTime start, DateTime end, int createUserId, GroupProcessType processType)
  2579. {
  2580. var jw = JsonView(false);
  2581. var positionConfig = new Dictionary<GroupProcessType, (int TemplateId, string ProcessName, string Rules, int? TaskAssignmentCTId)>
  2582. {
  2583. {
  2584. GroupProcessType.AirTicket,
  2585. (
  2586. TemplateId: 1464,
  2587. ProcessName: "机票",
  2588. Rules: @"1、初步拟定航程方案及价格 (建团后确认出团的时候开始24小时内)
  2589. 2、机票占位、续位
  2590. 3、完成机票采购确认(含预算核对、出票确认等)
  2591. 4、进行出票操作并核查信息
  2592. 5、机票已出(团组出发前5日)
  2593. 6、完成机票选座(出发前24小时)",
  2594. TaskAssignmentCTId: 85 // 机票需要筛选任务分配
  2595. )
  2596. },
  2597. {
  2598. GroupProcessType.Hotel,
  2599. (
  2600. TemplateId: 1465,
  2601. ProcessName: "酒店",
  2602. Rules: @"1、筛选并按照预算标准,对目标酒店进行询价、比价、谈价 (建团后确认出团的时候开始) 2个工作日
  2603. 2、获取酒店确认函与入住名单核对
  2604. 3、预订酒店并录入OA
  2605. 4、行前再次确认酒店订单、付款状态及入住安排(团组出发前5日)
  2606. 5、行程结束后整理酒店发票与结算(团组结束后5天内)",
  2607. TaskAssignmentCTId: null // 酒店不需要筛选任务分配
  2608. )
  2609. }
  2610. };
  2611. var supportedProcessTypes = positionConfig.Keys.ToArray();
  2612. if (!supportedProcessTypes.Contains(processType))
  2613. {
  2614. var supportedTypes = string.Join("、", supportedProcessTypes.Select(x => $"{positionConfig[x].ProcessName}({(int)x})"));
  2615. jw.Msg = $"不支持的流程类型!仅支持:{supportedTypes}";
  2616. return Ok(jw);
  2617. }
  2618. var config = positionConfig[processType];
  2619. var user_entity = _sqlSugar.Queryable<Sys_Users>()
  2620. .First(e => e.Id == userId && e.IsDel == 0);
  2621. if (user_entity == null)
  2622. {
  2623. jw.Msg = "用户不存在!";
  2624. return Ok(jw);
  2625. }
  2626. List<string> useridlist = new List<string> { user_entity.QiyeChatUserId };
  2627. // 获取考勤信息
  2628. var checkin_data = await _qiYeWeChatApiService.GetCheckinDataAsync(useridlist, 1, start, end);
  2629. if (checkin_data.errcode != 0)
  2630. {
  2631. jw.Msg = "获取企微上下班考勤打卡信息失败!" + checkin_data.errmsg;
  2632. return Ok(jw);
  2633. }
  2634. // AI绩效分析(考勤)
  2635. string question = string.Empty;
  2636. var settingTemp = await _sqlSugar.Queryable<Sys_SetData>().FirstAsync(x => x.Id == 1462 && x.IsDel == 0);
  2637. if (settingTemp != null && !string.IsNullOrEmpty(settingTemp.Remark))
  2638. {
  2639. var stringFormatResp = await GeneralMethod.StringFormatAsync(new StringFormatDto
  2640. {
  2641. FormatTemplate = settingTemp.Remark,
  2642. Parameters = new List<string> {
  2643. JsonConvert.SerializeObject(checkin_data.checkindata),
  2644. user_entity.CnName
  2645. }
  2646. });
  2647. if (stringFormatResp.Success)
  2648. {
  2649. question = stringFormatResp.FormattedResult;
  2650. }
  2651. else
  2652. {
  2653. jw.Msg = "获取ai绩效分析模板失败!" + stringFormatResp.Message;
  2654. return Ok(jw);
  2655. }
  2656. }
  2657. var kaoqinResp = await _deepSeekService.ChatAsync(question);
  2658. if (!kaoqinResp.Success)
  2659. {
  2660. jw.Msg = "Ai分析用户绩效失败!" + kaoqinResp.Message;
  2661. return Ok(jw);
  2662. }
  2663. // 检索时间段团组信息
  2664. var group_data = await _sqlSugar.Queryable<Grp_DelegationInfo>()
  2665. .Where(x => x.IsDel == 0)
  2666. .Where(x => SqlFunc.Between(x.VisitDate, start, end.AddDays(1)))
  2667. .Select(x => new
  2668. {
  2669. x.TeamName,
  2670. x.VisitDate,
  2671. x.Id,
  2672. CreateTime = x.CreateTime,
  2673. x.StepOperationTime,
  2674. })
  2675. .ToListAsync();
  2676. if (config.TaskAssignmentCTId.HasValue)
  2677. {
  2678. string sqlAssignment = @$"
  2679. SELECT DIId FROM Grp_GroupsTaskAssignment
  2680. WHERE isdel = 0 and CTId = {config.TaskAssignmentCTId.Value} and UId = {userId}
  2681. order by id desc ";
  2682. var assignmentList = await _sqlSugar.Ado.SqlQueryAsync<int>(sqlAssignment);
  2683. group_data = group_data.Where(x => assignmentList.Contains(x.Id)).ToList();
  2684. }
  2685. // 查询每个团的团组进程
  2686. string group_process_question = string.Empty;
  2687. string group_process_data = string.Empty;
  2688. foreach (var item in group_data)
  2689. {
  2690. var group_process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
  2691. .Where(x => x.IsDel == 0 && x.GroupId == item.Id && x.ProcessType == processType)
  2692. .FirstAsync();
  2693. if (group_process != null)
  2694. {
  2695. var detailList = await _sqlSugar.Queryable<Grp_ProcessNode>()
  2696. .Where(x => x.IsDel == 0 && x.ProcessId == group_process.ProcessOrder)
  2697. .Select(x => new
  2698. {
  2699. x.NodeName,
  2700. x.OverallStatus,
  2701. x.NodeDescTips,
  2702. x.ActualDone,
  2703. })
  2704. .ToListAsync();
  2705. var startTime = item.VisitDate.ToString("yyyy - MM - dd");
  2706. if (item.StepOperationTime.HasValue && item.StepOperationTime != DateTime.MinValue)
  2707. startTime = item.StepOperationTime.Value.ToString("yyyy - MM - dd");
  2708. group_process_data += $"团组名称:{item.TeamName}," +
  2709. $"出访日期:{startTime},团组创建时间:{item.CreateTime.ToString("yyyy - MM - dd HH:mm:ss")},团组{config.ProcessName}进程信息如下:{JsonConvert.SerializeObject(detailList)}\r\n\r\n";
  2710. }
  2711. }
  2712. if (string.IsNullOrEmpty(group_process_data))
  2713. {
  2714. group_process_data = "该员工没有负责的团组!";
  2715. }
  2716. // 构建AI分析问题
  2717. group_process_question += @$"团组数据:
  2718. {group_process_data}
  2719. 团组规则:
  2720. {config.Rules}
  2721. 根据我提供的团组数据以及规则(ActualDone字段为实际完成时间,OverallStatus字段为节点状态 1=未开始,2=进行中,3=已完成),判断是否按时完成每个环节(没有指定时间则只要在团组出发前完成即可),并且分析该员工{user_entity.CnName}的工作情况。";
  2722. // 使用模板格式化问题(如果存在)
  2723. try
  2724. {
  2725. var str = await _sqlSugar.Queryable<Sys_SetData>().FirstAsync(x => x.Id == config.TemplateId && x.IsDel == 0);
  2726. if (str != null && !string.IsNullOrEmpty(str.Remark))
  2727. {
  2728. var stringFormatResp = await GeneralMethod.StringFormatAsync(new StringFormatDto
  2729. {
  2730. FormatTemplate = str.Remark,
  2731. Parameters = new List<string> {
  2732. group_process_data,
  2733. user_entity.CnName
  2734. }
  2735. });
  2736. if (stringFormatResp.Success)
  2737. {
  2738. //日志
  2739. _logger.LogInformation($"模板格式化成功,模板ID:{config.TemplateId}, 时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
  2740. group_process_question = stringFormatResp.FormattedResult;
  2741. }
  2742. else
  2743. {
  2744. _logger.LogError($"模板格式化失败,模板ID:{config.TemplateId}, 时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}, 错误:{stringFormatResp.Message}");
  2745. }
  2746. }
  2747. }
  2748. catch (Exception ex)
  2749. {
  2750. jw.Msg = "模板格式化失败!" + ex.Message;
  2751. return Ok(jw);
  2752. }
  2753. // AI分析团组进程
  2754. var group_process_resp = await _deepSeekService.ChatAsync(group_process_question);
  2755. if (!group_process_resp.Success)
  2756. {
  2757. jw.Msg = $"Ai分析用户{config.ProcessName}进程失败!" + group_process_resp.Message;
  2758. return Ok(jw);
  2759. }
  2760. jw.Code = 200;
  2761. jw.Msg = "操作成功!";
  2762. jw.Data = new AiResponse()
  2763. {
  2764. Answer = group_process_resp.Answer,
  2765. KaoqinAnswer = kaoqinResp.Answer,
  2766. };
  2767. // 保存至数据库中
  2768. Pm_PerformanceAnalysis insertData = new Pm_PerformanceAnalysis
  2769. {
  2770. CreateTime = DateTime.Now,
  2771. CreateUserId = createUserId,
  2772. IsDel = 0,
  2773. Year = start.Year,
  2774. Month = start.Month,
  2775. JsonResult = JsonConvert.SerializeObject(jw.Data),
  2776. UserId = userId,
  2777. };
  2778. await _sqlSugar.Insertable(insertData).ExecuteCommandAsync();
  2779. return Ok(jw);
  2780. }
  2781. /// <summary>
  2782. /// 机票员工分析
  2783. /// </summary>
  2784. [HttpGet]
  2785. public async Task<IActionResult> AiPerformanceAnalysis_AirTicketAsync(int userId, DateTime start, DateTime end, int createUserId)
  2786. {
  2787. return await AiPerformanceAnalysisAsync(userId, start, end, createUserId, GroupProcessType.AirTicket);
  2788. }
  2789. /// <summary>
  2790. /// 酒店员工分析
  2791. /// </summary>
  2792. [HttpGet]
  2793. public async Task<IActionResult> AiPerformanceAnalysis_HotelAsync(int userId, DateTime start, DateTime end, int createUserId)
  2794. {
  2795. return await AiPerformanceAnalysisAsync(userId, start, end, createUserId, GroupProcessType.Hotel);
  2796. }
  2797. /// <summary>
  2798. /// 获取考勤数据并进行AI分析
  2799. /// </summary>
  2800. /// <param name="user_entity">用户实体</param>
  2801. /// <param name="start">开始时间</param>
  2802. /// <param name="end">结束时间</param>
  2803. /// <param name="jw">JsonView对象,用于返回错误信息</param>
  2804. /// <param name="errorMessagePrefix">错误消息前缀(用于区分不同的分析类型)</param>
  2805. /// <returns>考勤分析结果,失败时返回null</returns>
  2806. private async Task<string> GetKaoqinAnalysisAsync(Sys_Users user_entity, DateTime start, DateTime end, JsonView jw, string errorMessagePrefix = "Ai分析用户绩效")
  2807. {
  2808. List<string> useridlist = new List<string> { user_entity.QiyeChatUserId };
  2809. var checkin_data = await _qiYeWeChatApiService.GetCheckinDataAsync(useridlist, 1, start, end);
  2810. if (checkin_data.errcode != 0)
  2811. {
  2812. jw.Msg = "获取企微上下班考勤打卡信息失败!" + checkin_data.errmsg;
  2813. return null;
  2814. }
  2815. string question = string.Empty;
  2816. var settingTemp = await _sqlSugar.Queryable<Sys_SetData>().FirstAsync(x => x.Id == 1462 && x.IsDel == 0);
  2817. if (settingTemp != null && !string.IsNullOrEmpty(settingTemp.Remark))
  2818. {
  2819. var stringFormatResp = await GeneralMethod.StringFormatAsync(new StringFormatDto
  2820. {
  2821. FormatTemplate = settingTemp.Remark,
  2822. Parameters = new List<string> {
  2823. JsonConvert.SerializeObject(checkin_data.checkindata),
  2824. user_entity.CnName
  2825. }
  2826. });
  2827. if (stringFormatResp.Success)
  2828. {
  2829. question = stringFormatResp.FormattedResult;
  2830. }
  2831. else
  2832. {
  2833. jw.Msg = "获取ai绩效分析模板失败!" + stringFormatResp.Message;
  2834. return null;
  2835. }
  2836. }
  2837. var kaoqinResp = await _deepSeekService.ChatAsync(question);
  2838. if (!kaoqinResp.Success)
  2839. {
  2840. jw.Msg = $"{errorMessagePrefix}失败!" + kaoqinResp.Message;
  2841. return null;
  2842. }
  2843. return kaoqinResp.Answer;
  2844. }
  2845. /// <summary>
  2846. /// 签证员工分析
  2847. /// </summary>
  2848. /// <param name="userId"></param>
  2849. /// <param name="start"></param>
  2850. /// <param name="end"></param>
  2851. /// <param name="createUserId"></param>
  2852. /// <returns></returns>
  2853. [HttpGet]
  2854. public async Task<IActionResult> AiPerformanceAnalysis_VisaAsync(int userId, DateTime start, DateTime end, int createUserId)
  2855. {
  2856. var jw = JsonView(false);
  2857. // 验证用户
  2858. var user_entity = await _sqlSugar.Queryable<Sys_Users>()
  2859. .FirstAsync(e => e.Id == userId && e.IsDel == 0);
  2860. if (user_entity == null)
  2861. {
  2862. jw.Msg = "用户不存在!";
  2863. return Ok(jw);
  2864. }
  2865. // 获取考勤分析结果
  2866. var kaoqinAnswer = await GetKaoqinAnalysisAsync(user_entity, start, end, jw, "Ai分析签证考勤");
  2867. if (kaoqinAnswer == null)
  2868. {
  2869. return Ok(jw);
  2870. }
  2871. // 检索时间段团组信息
  2872. var group_data = await _sqlSugar.Queryable<Grp_DelegationInfo>()
  2873. .Where(x => x.IsDel == 0)
  2874. .Where(x => SqlFunc.Between(x.VisitDate, start, end.AddDays(1)))
  2875. .Select(x => new
  2876. {
  2877. x.TeamName,
  2878. x.VisitDate,
  2879. x.Id,
  2880. CreateTime = x.CreateTime,
  2881. x.StepOperationTime,
  2882. })
  2883. .ToListAsync();
  2884. string group_process_question = string.Empty;
  2885. string group_process_data = string.Empty;
  2886. foreach (var item in group_data)
  2887. {
  2888. var group_process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
  2889. .Where(x => x.IsDel == 0 && x.GroupId == item.Id && x.ProcessType == GroupProcessType.Visa)
  2890. .FirstAsync();
  2891. //获取团组倒推表数据
  2892. var back_data = await _sqlSugar.Queryable<Grp_InvertedList>()
  2893. .Where(x => x.IsDel == 0 && x.DiId == item.Id)
  2894. .ToListAsync();
  2895. var startTime = item.VisitDate.ToString("yyyy - MM - dd");
  2896. if (item.StepOperationTime.HasValue && item.StepOperationTime != DateTime.MinValue)
  2897. startTime = item.StepOperationTime.Value.ToString("yyyy - MM - dd");
  2898. group_process_data += $"团组名称:{item.TeamName}," +
  2899. $"出访日期:{startTime},团组创建时间:{item.CreateTime.ToString("yyyy - MM - dd HH:mm:ss")},团组签证进程信息如下:{JsonConvert.SerializeObject(group_process)}\r\n\r\n";
  2900. if (back_data.Count > 0)
  2901. {
  2902. group_process_data += $"该团组倒退表信息如下:{JsonConvert.SerializeObject(back_data)}\r\n\r\n";
  2903. }
  2904. else
  2905. {
  2906. group_process_data += $"该团组倒退表信息为空!\r\n\r\n";
  2907. }
  2908. if (group_process != null)
  2909. {
  2910. group_process_data += $"团组签证进程时间如下:{JsonConvert.SerializeObject(group_process)}\r\n\r\n";
  2911. }
  2912. else
  2913. {
  2914. group_process_data += $"团组签证进程时间为空!\r\n\r\n";
  2915. }
  2916. }
  2917. group_process_question += @$"团组数据:
  2918. {group_process_data}
  2919. 根据倒推表和团组签证进程时间做匹配,做超时等判断,总结分析出该员工{user_entity.CnName}的工作饱和度、工作详细完成度、工作效率、完成度、风险点、问题出现的地方及改进建议";
  2920. //处理模板
  2921. try
  2922. {
  2923. var str = await _sqlSugar.Queryable<Sys_SetData>().FirstAsync(x => x.Id == 1467 && x.IsDel == 0);
  2924. if (str != null && !string.IsNullOrEmpty(str.Remark))
  2925. {
  2926. var stringFormatResp = await GeneralMethod.StringFormatAsync(new StringFormatDto
  2927. {
  2928. FormatTemplate = str.Remark,
  2929. Parameters = new List<string> {
  2930. group_process_data,
  2931. user_entity.CnName
  2932. }
  2933. });
  2934. if (stringFormatResp.Success)
  2935. {
  2936. group_process_question = stringFormatResp.FormattedResult;
  2937. }
  2938. else
  2939. {
  2940. _logger.LogError($"签证模板格式化失败,模板ID:{1467}, 时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}, 错误:{stringFormatResp.Message}");
  2941. }
  2942. }
  2943. }
  2944. catch (Exception ex)
  2945. {
  2946. jw.Msg = "模板格式化失败!" + ex.Message;
  2947. return Ok(jw);
  2948. }
  2949. var group_process_resp = await _deepSeekService.ChatAsync(group_process_question);
  2950. if (!group_process_resp.Success)
  2951. {
  2952. jw.Msg = "Ai分析用户签证进程失败!" + group_process_resp.Message;
  2953. return Ok(jw);
  2954. }
  2955. jw.Code = 200;
  2956. jw.Msg = "操作成功!";
  2957. jw.Data = new AiResponse()
  2958. {
  2959. Answer = group_process_resp.Answer,
  2960. KaoqinAnswer = kaoqinAnswer,
  2961. };
  2962. //保存至数据库中
  2963. Pm_PerformanceAnalysis insertData = new Pm_PerformanceAnalysis
  2964. {
  2965. CreateTime = DateTime.Now,
  2966. CreateUserId = createUserId,
  2967. IsDel = 0,
  2968. Year = start.Year,
  2969. Month = start.Month,
  2970. JsonResult = JsonConvert.SerializeObject(jw.Data),
  2971. UserId = userId,
  2972. };
  2973. await _sqlSugar.Insertable(insertData).ExecuteCommandAsync();
  2974. return Ok(jw);
  2975. }
  2976. /// <summary>
  2977. /// OP员工分析
  2978. /// </summary>
  2979. /// <param name="userId"></param>
  2980. /// <param name="start"></param>
  2981. /// <param name="end"></param>
  2982. /// <param name="createUserId"></param>
  2983. /// <returns></returns>
  2984. [HttpGet]
  2985. public async Task<IActionResult> AiPerformanceAnalysis_OPAsync(int userId, DateTime start, DateTime end, int createUserId)
  2986. {
  2987. var jw = JsonView(false);
  2988. // 验证用户
  2989. var user_entity = await _sqlSugar.Queryable<Sys_Users>()
  2990. .FirstAsync(e => e.Id == userId && e.IsDel == 0);
  2991. if (user_entity == null)
  2992. {
  2993. jw.Msg = "用户不存在!";
  2994. return Ok(jw);
  2995. }
  2996. // 获取考勤分析结果
  2997. var kaoqinAnswer = await GetKaoqinAnalysisAsync(user_entity, start, end, jw, "Ai分析OP考勤");
  2998. if (kaoqinAnswer == null)
  2999. {
  3000. return Ok(jw);
  3001. }
  3002. //检索负责的团组
  3003. var group_data = await _sqlSugar.Queryable<Grp_DelegationInfo>()
  3004. .Where(x => x.IsDel == 0)
  3005. .Where(x => SqlFunc.Between(x.VisitDate, start, end.AddDays(1)))
  3006. .ToListAsync();
  3007. string sqlAssignment = @$"
  3008. SELECT DIId FROM Grp_GroupsTaskAssignment
  3009. WHERE isdel = 0 and CTId = 79 and UId = {userId}
  3010. order by id desc ";
  3011. var assignmentList = await _sqlSugar.Ado.SqlQueryAsync<int>(sqlAssignment);
  3012. group_data = group_data.Where(x => assignmentList.Contains(x.Id)).ToList();
  3013. string group_process_question = string.Empty;
  3014. string group_process_data = string.Empty;
  3015. var back_group_list = await _sqlSugar.Queryable<Grp_InvertedList>()
  3016. .Where(x => x.IsDel == 0)
  3017. .Where(x => group_data.Select(y => y.Id).ToList().Contains(x.DiId))
  3018. .ToListAsync();
  3019. var process_group_list = await _sqlSugar.Queryable<Grp_ProcessOverview>()
  3020. .Where(x => x.IsDel == 0)
  3021. .Where(x => group_data.Select(y => y.Id).ToList().Contains(x.GroupId))
  3022. .ToListAsync();
  3023. foreach (var item in group_data)
  3024. {
  3025. //获取团组倒退表
  3026. var back_data = back_group_list.Where(x => x.DiId == item.Id).ToList();
  3027. //获取团组进程
  3028. var process_data = process_group_list.Where(x => x.GroupId == item.Id).ToList();
  3029. }
  3030. //检索黑屏代码最后的时间
  3031. //业务分析
  3032. jw.Code = 200;
  3033. jw.Msg = "操作成功!";
  3034. jw.Data = new AiResponse()
  3035. {
  3036. KaoqinAnswer = kaoqinAnswer,
  3037. };
  3038. return Ok(jw);
  3039. }
  3040. class wordTable
  3041. {
  3042. public int 序号 { get; set; }
  3043. public string 团组名 { get; set; }
  3044. public decimal 营业颔 { get; set; }
  3045. public string 出访日期 { get; set; }
  3046. public int 人数 { get; set; }
  3047. // public string 收款日期 { get; set; }
  3048. };
  3049. /// <summary>
  3050. /// 市场部绩效pdf下载
  3051. /// </summary>
  3052. /// <param name="year"></param>
  3053. /// <param name="month"></param>
  3054. /// <param name="userId"></param>
  3055. /// <returns></returns>
  3056. [HttpPost]
  3057. public async Task<IActionResult> AiPerformanceAnalysis_JobMarketingFileDownAsync(int year, int month, int userId)
  3058. {
  3059. var jw = JsonView(false);
  3060. var user_entity = _sqlSugar.Queryable<Sys_Users>()
  3061. .First(e => e.Id == userId && e.IsDel == 0);
  3062. if (user_entity == null)
  3063. {
  3064. jw.Msg = "用户不存在!";
  3065. return Ok(jw);
  3066. }
  3067. var data = new Pm_PerformanceAnalysis();
  3068. if (year < 1 && month < 1)
  3069. {
  3070. data = await _sqlSugar.Queryable<Pm_PerformanceAnalysis>()
  3071. .Where(x => x.UserId == userId && x.IsDel == 0)
  3072. .OrderByDescending(x => x.CreateTime)
  3073. .FirstAsync();
  3074. }
  3075. else
  3076. {
  3077. data = await _sqlSugar.Queryable<Pm_PerformanceAnalysis>()
  3078. .Where(x => x.UserId == userId && x.Year == year && x.Month == month && x.IsDel == 0)
  3079. .OrderByDescending(x => x.CreateTime)
  3080. .FirstAsync();
  3081. }
  3082. if (data == null)
  3083. {
  3084. jw.Msg = "数据不存在!";
  3085. return Ok(jw);
  3086. }
  3087. var jsonResult = JsonConvert.DeserializeObject<AiResponse>(data.JsonResult) ?? new AiResponse();
  3088. var tableapi = await this.AiPerformanceAnalysis_GroupStatisticsAsync(
  3089. userId,
  3090. new DateTime(data.Year, 1, 1),
  3091. new DateTime(data.Year, 1, 1).AddYears(1)
  3092. );
  3093. var jwValue = (((tableapi as OkObjectResult).Value) as OASystem.Domain.ViewModels.JsonView);
  3094. if (jwValue.Code != 200)
  3095. {
  3096. jw.Msg = "获取团组统计数据失败!" + jwValue.Msg;
  3097. return Ok(jw);
  3098. }
  3099. var tableList = jwValue.Data as List<AiPerformanceAnalysis_GroupStatisticsView>;
  3100. if (tableList == null)
  3101. {
  3102. jw.Msg = "获取团组统计数据失败!";
  3103. return Ok(jw);
  3104. }
  3105. var tableListValue = tableList.Select(x => new wordTable
  3106. {
  3107. 序号 = x.RowNumber,
  3108. 团组名 = x.TeamName,
  3109. 营业颔 = x.GroupSales,
  3110. 出访日期 = x.VisitDate.ToString("yyyy-MM-dd"),
  3111. 人数 = x.VisitPNumber,
  3112. // 收款日期 = x.CollectionDays.ToString("yyyy-MM-dd")
  3113. })
  3114. .OrderBy(x => x.序号)
  3115. .ToList();
  3116. var url = this.MarkdownToWord(new
  3117. List<WordContentItem>{
  3118. new
  3119. WordContentItem{
  3120. Type = WordContentType.Markdown,
  3121. MarkdownContent = jsonResult.Answer
  3122. },
  3123. WordContentItem.FromObjectList(tableListValue, "团组统计")
  3124. ,
  3125. new
  3126. WordContentItem{
  3127. Type = WordContentType.Markdown,
  3128. MarkdownContent = jsonResult.KaoqinAnswer
  3129. }
  3130. }, $"{user_entity.CnName}_{data.Year}年{data.Month.ToString("00")}月绩效分析");
  3131. jw.Data = new { url };
  3132. jw.Code = 200;
  3133. jw.Msg = "操作成功!";
  3134. return Ok(jw);
  3135. }
  3136. /// <summary>
  3137. /// 国交员工分析pdf下载
  3138. /// </summary>
  3139. /// <param name="year"></param>
  3140. /// <param name="month"></param>
  3141. /// <param name="userId"></param>
  3142. /// <returns></returns>
  3143. [HttpPost]
  3144. public async Task<IActionResult> AiPerformanceAnalysis_GuojiaoFileDownAsync(int year, int month, int userId)
  3145. {
  3146. var jw = JsonView(false);
  3147. var user_entity = _sqlSugar.Queryable<Sys_Users>()
  3148. .First(e => e.Id == userId && e.IsDel == 0);
  3149. if (user_entity == null)
  3150. {
  3151. jw.Msg = "用户不存在!";
  3152. return Ok(jw);
  3153. }
  3154. var data = new Pm_PerformanceAnalysis();
  3155. if (year < 1 && month < 1)
  3156. {
  3157. data = await _sqlSugar.Queryable<Pm_PerformanceAnalysis>()
  3158. .Where(x => x.UserId == userId && x.IsDel == 0)
  3159. .OrderByDescending(x => x.CreateTime)
  3160. .FirstAsync();
  3161. }
  3162. else
  3163. {
  3164. data = await _sqlSugar.Queryable<Pm_PerformanceAnalysis>()
  3165. .Where(x => x.UserId == userId && x.Year == year && x.Month == month && x.IsDel == 0)
  3166. .OrderByDescending(x => x.CreateTime)
  3167. .FirstAsync();
  3168. }
  3169. if (data == null)
  3170. {
  3171. jw.Msg = "数据不存在!";
  3172. return Ok(jw);
  3173. }
  3174. var jsonResult = JsonConvert.DeserializeObject<AiResponse>(data.JsonResult) ?? new AiResponse();
  3175. var url = this.MarkdownToWord(new
  3176. List<WordContentItem>{
  3177. new
  3178. WordContentItem{
  3179. Type = WordContentType.Markdown,
  3180. MarkdownContent = jsonResult.Answer
  3181. },
  3182. new
  3183. WordContentItem{
  3184. Type = WordContentType.Markdown,
  3185. MarkdownContent = jsonResult.KaoqinAnswer
  3186. }
  3187. }, $"{user_entity.CnName}_{data.Year}年{data.Month.ToString("00")}月绩效分析");
  3188. jw.Data = new { url };
  3189. jw.Code = 200;
  3190. jw.Msg = "操作成功!";
  3191. return Ok(jw);
  3192. }
  3193. /// <summary>
  3194. /// Word文档内容项类型
  3195. /// </summary>
  3196. public enum WordContentType
  3197. {
  3198. Markdown, // Markdown内容
  3199. Table // 表格内容
  3200. }
  3201. /// <summary>
  3202. /// Word文档内容项
  3203. /// </summary>
  3204. public class WordContentItem
  3205. {
  3206. /// <summary>
  3207. /// 内容类型
  3208. /// </summary>
  3209. public WordContentType Type { get; set; }
  3210. /// <summary>
  3211. /// Markdown内容(当Type为Markdown时使用)
  3212. /// </summary>
  3213. public string MarkdownContent { get; set; }
  3214. /// <summary>
  3215. /// 表格数据(当Type为Table时使用)- 第一行为表头
  3216. /// </summary>
  3217. public List<List<string>> TableData { get; set; }
  3218. /// <summary>
  3219. /// 表格标题(可选)
  3220. /// </summary>
  3221. public string TableTitle { get; set; }
  3222. /// <summary>
  3223. /// 从DataTable创建表格数据(便捷方法)
  3224. /// </summary>
  3225. /// <param name="dataTable">数据表</param>
  3226. /// <param name="tableTitle">表格标题(可选)</param>
  3227. /// <param name="includeHeader">是否包含表头(默认true)</param>
  3228. /// <returns>WordContentItem实例</returns>
  3229. public static WordContentItem FromDataTable(DataTable dataTable, string? tableTitle = null, bool includeHeader = true)
  3230. {
  3231. if (dataTable == null || dataTable.Rows.Count == 0)
  3232. {
  3233. return new WordContentItem
  3234. {
  3235. Type = WordContentType.Table,
  3236. TableTitle = tableTitle,
  3237. TableData = new List<List<string>>()
  3238. };
  3239. }
  3240. var tableData = new List<List<string>>();
  3241. // 添加表头
  3242. if (includeHeader)
  3243. {
  3244. var headerRow = new List<string>();
  3245. foreach (DataColumn column in dataTable.Columns)
  3246. {
  3247. headerRow.Add(column.ColumnName);
  3248. }
  3249. tableData.Add(headerRow);
  3250. }
  3251. // 添加数据行
  3252. foreach (DataRow row in dataTable.Rows)
  3253. {
  3254. var dataRow = new List<string>();
  3255. foreach (DataColumn column in dataTable.Columns)
  3256. {
  3257. dataRow.Add(row[column]?.ToString() ?? "");
  3258. }
  3259. tableData.Add(dataRow);
  3260. }
  3261. return new WordContentItem
  3262. {
  3263. Type = WordContentType.Table,
  3264. TableTitle = tableTitle,
  3265. TableData = tableData
  3266. };
  3267. }
  3268. /// <summary>
  3269. /// 从对象列表创建表格数据(便捷方法)
  3270. /// </summary>
  3271. /// <typeparam name="T">对象类型</typeparam>
  3272. /// <param name="items">对象列表</param>
  3273. /// <param name="tableTitle">表格标题(可选)</param>
  3274. /// <param name="columnMappings">列映射(属性名 -> 显示名称),如果为null则使用属性名</param>
  3275. /// <returns>WordContentItem实例</returns>
  3276. public static WordContentItem FromObjectList<T>(List<T> items, string? tableTitle = null, Dictionary<string, string>? columnMappings = null)
  3277. {
  3278. if (items == null || items.Count == 0)
  3279. {
  3280. return new WordContentItem
  3281. {
  3282. Type = WordContentType.Table,
  3283. TableTitle = tableTitle,
  3284. TableData = new List<List<string>>()
  3285. };
  3286. }
  3287. var tableData = new List<List<string>>();
  3288. var properties = typeof(T).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
  3289. // 添加表头
  3290. var headerRow = new List<string>();
  3291. foreach (var prop in properties)
  3292. {
  3293. string headerName = columnMappings != null && columnMappings.ContainsKey(prop.Name)
  3294. ? columnMappings[prop.Name]
  3295. : prop.Name;
  3296. headerRow.Add(headerName);
  3297. }
  3298. tableData.Add(headerRow);
  3299. // 添加数据行
  3300. foreach (var item in items)
  3301. {
  3302. var dataRow = new List<string>();
  3303. foreach (var prop in properties)
  3304. {
  3305. var value = prop.GetValue(item);
  3306. dataRow.Add(value?.ToString() ?? "");
  3307. }
  3308. tableData.Add(dataRow);
  3309. }
  3310. return new WordContentItem
  3311. {
  3312. Type = WordContentType.Table,
  3313. TableTitle = tableTitle,
  3314. TableData = tableData
  3315. };
  3316. }
  3317. }
  3318. /// <summary>
  3319. /// 将多个Markdown内容和表格合并转换为Word文档
  3320. /// </summary>
  3321. /// <param name="contentItems">内容项列表(可以是Markdown或表格)</param>
  3322. /// <param name="fileName">文件名(不含扩展名)</param>
  3323. /// <returns>返回文件访问URL</returns>
  3324. private string MarkdownToWord(List<WordContentItem> contentItems, string fileName)
  3325. {
  3326. if (contentItems == null || contentItems.Count == 0)
  3327. {
  3328. throw new ArgumentException("内容项不能为空", nameof(contentItems));
  3329. }
  3330. // 创建新的Word文档
  3331. Aspose.Words.Document doc = new Aspose.Words.Document();
  3332. Aspose.Words.DocumentBuilder builder = new Aspose.Words.DocumentBuilder(doc);
  3333. // 设置默认字体
  3334. builder.Font.Name = "微软雅黑";
  3335. builder.Font.Size = 10.5;
  3336. // Markdown转换管道
  3337. var pipeline = new Markdig.MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
  3338. // 用于记录每次插入内容后的目标段落位置
  3339. Aspose.Words.Paragraph targetParagraph = null;
  3340. // 处理每个内容项
  3341. for (int i = 0; i < contentItems.Count; i++)
  3342. {
  3343. var item = contentItems[i];
  3344. // 如果是第一个内容项,先清理文档开头的空段落
  3345. if (i == 0)
  3346. {
  3347. // 清理文档开头的所有空段落
  3348. var firstBody = doc.FirstSection.Body;
  3349. while (firstBody.Paragraphs.Count > 0)
  3350. {
  3351. var firstPara = firstBody.Paragraphs[0];
  3352. if (string.IsNullOrWhiteSpace(firstPara.GetText().Trim()))
  3353. {
  3354. firstPara.Remove();
  3355. }
  3356. else
  3357. {
  3358. break;
  3359. }
  3360. }
  3361. }
  3362. else
  3363. {
  3364. // 如果不是第一个内容项,移动到目标段落位置
  3365. if (targetParagraph != null)
  3366. {
  3367. builder.MoveTo(targetParagraph);
  3368. }
  3369. else
  3370. {
  3371. builder.MoveToDocumentEnd();
  3372. }
  3373. }
  3374. if (item.Type == WordContentType.Markdown)
  3375. {
  3376. // 处理Markdown内容
  3377. if (!string.IsNullOrWhiteSpace(item.MarkdownContent))
  3378. {
  3379. // 将Markdown转换为HTML
  3380. string htmlBody = Markdig.Markdown.ToHtml(item.MarkdownContent, pipeline);
  3381. // 包装成完整的HTML片段,减少body的margin和padding
  3382. string htmlContent = $@"<!DOCTYPE html>
  3383. <html>
  3384. <head>
  3385. <meta charset=""UTF-8"">
  3386. <meta http-equiv=""Content-Type"" content=""text/html; charset=UTF-8"">
  3387. <style>
  3388. body {{ font-family: '微软雅黑', 'Microsoft YaHei', SimSun, sans-serif; font-size: 10.5pt; line-height: 1.5; margin: 0; padding: 0; }}
  3389. h1, h2, h3, h4, h5, h6 {{ font-family: '微软雅黑', 'Microsoft YaHei', SimHei, sans-serif; margin-top: 12pt; margin-bottom: 6pt; }}
  3390. h1 {{ margin-top: 0; }}
  3391. p {{ margin: 0; padding: 0; }}
  3392. table {{ border-collapse: collapse; width: 100%; margin: 10px 0; }}
  3393. table th, table td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
  3394. table th {{ background-color: #f2f2f2; font-weight: bold; }}
  3395. code {{ font-family: 'Consolas', 'Courier New', monospace; }}
  3396. pre {{ background-color: #f5f5f5; padding: 10px; border-radius: 4px; margin: 6pt 0; }}
  3397. ul, ol {{ margin: 6pt 0; padding-left: 20pt; }}
  3398. li {{ margin: 3pt 0; }}
  3399. </style>
  3400. </head>
  3401. <body>
  3402. {htmlBody}
  3403. </body>
  3404. </html>";
  3405. // 使用LoadOptions指定UTF-8编码
  3406. var loadOptions = new Aspose.Words.LoadOptions
  3407. {
  3408. LoadFormat = Aspose.Words.LoadFormat.Html,
  3409. Encoding = System.Text.Encoding.UTF8
  3410. };
  3411. // 从HTML创建临时文档
  3412. byte[] htmlBytes = System.Text.Encoding.UTF8.GetBytes(htmlContent);
  3413. using (MemoryStream htmlStream = new MemoryStream(htmlBytes))
  3414. {
  3415. Aspose.Words.Document tempDoc = new Aspose.Words.Document(htmlStream, loadOptions);
  3416. if (i == 0)
  3417. {
  3418. // 第一个内容项:先清理所有空段落
  3419. var firstBody = doc.FirstSection.Body;
  3420. // 删除所有空段落
  3421. while (firstBody.Paragraphs.Count > 0)
  3422. {
  3423. var firstPara = firstBody.Paragraphs[0];
  3424. if (string.IsNullOrWhiteSpace(firstPara.GetText().Trim()))
  3425. {
  3426. firstPara.Remove();
  3427. }
  3428. else
  3429. {
  3430. break;
  3431. }
  3432. }
  3433. // 如果文档完全为空,直接替换整个文档内容
  3434. if (firstBody.Paragraphs.Count == 0)
  3435. {
  3436. // 清空原文档的所有内容
  3437. doc.FirstSection.Body.RemoveAllChildren();
  3438. // 将临时文档的所有内容节点复制到主文档
  3439. foreach (Aspose.Words.Node node in tempDoc.FirstSection.Body.ChildNodes.ToArray())
  3440. {
  3441. var importedNode = doc.ImportNode(node, true);
  3442. doc.FirstSection.Body.AppendChild(importedNode);
  3443. }
  3444. }
  3445. else
  3446. {
  3447. // 文档有内容,在第一个段落之前插入
  3448. var firstPara = firstBody.Paragraphs[0];
  3449. builder.MoveTo(firstPara);
  3450. builder.InsertDocument(tempDoc, Aspose.Words.ImportFormatMode.KeepSourceFormatting);
  3451. }
  3452. // 插入后再次清理开头的空段落,并统一格式
  3453. firstBody = doc.FirstSection.Body;
  3454. while (firstBody.Paragraphs.Count > 0)
  3455. {
  3456. var firstPara = firstBody.Paragraphs[0];
  3457. if (string.IsNullOrWhiteSpace(firstPara.GetText().Trim()))
  3458. {
  3459. firstPara.Remove();
  3460. }
  3461. else
  3462. {
  3463. // 找到第一个有内容的段落,确保它没有前间距
  3464. firstPara.ParagraphFormat.SpaceBefore = 0;
  3465. if (firstPara.ParagraphFormat.SpaceAfter > 6)
  3466. {
  3467. firstPara.ParagraphFormat.SpaceAfter = 6;
  3468. }
  3469. // 统一字体格式
  3470. foreach (Aspose.Words.Run run in firstPara.Runs)
  3471. {
  3472. if (string.IsNullOrEmpty(run.Font.Name) ||
  3473. (!run.Font.Name.Contains("微软") && !run.Font.Name.Contains("Microsoft") &&
  3474. !run.Font.Name.Contains("Sim") && !run.Font.Name.Contains("宋体")))
  3475. {
  3476. run.Font.Name = "微软雅黑";
  3477. }
  3478. if (firstPara.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading1 &&
  3479. firstPara.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading2 &&
  3480. firstPara.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading3 &&
  3481. firstPara.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading4 &&
  3482. (run.Font.Size == 0 || (run.Font.Size != 12 && run.Font.Size != 14 && run.Font.Size != 16 && run.Font.Size != 18 && run.Font.Size != 20 && run.Font.Size != 24)))
  3483. {
  3484. run.Font.Size = 10.5;
  3485. }
  3486. }
  3487. break;
  3488. }
  3489. }
  3490. // 统一处理第一个内容项的所有段落格式
  3491. foreach (Aspose.Words.Paragraph para in firstBody.Paragraphs)
  3492. {
  3493. if (string.IsNullOrWhiteSpace(para.GetText().Trim()))
  3494. {
  3495. continue;
  3496. }
  3497. // 统一段落格式
  3498. para.ParagraphFormat.SpaceBefore = 0;
  3499. if (para.ParagraphFormat.SpaceAfter > 6)
  3500. {
  3501. para.ParagraphFormat.SpaceAfter = 6;
  3502. }
  3503. // 统一字体格式 - 与非第一个内容项的处理逻辑完全一致
  3504. foreach (Aspose.Words.Run run in para.Runs)
  3505. {
  3506. // 统一字体名称 - 强制设置为微软雅黑
  3507. if (string.IsNullOrEmpty(run.Font.Name) ||
  3508. (!run.Font.Name.Contains("微软") && !run.Font.Name.Contains("Microsoft") &&
  3509. !run.Font.Name.Contains("Sim") && !run.Font.Name.Contains("宋体")))
  3510. {
  3511. run.Font.Name = "微软雅黑";
  3512. }
  3513. // 统一字体大小 - 与非第一个内容项的逻辑完全一致
  3514. bool isHeading = para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading1 ||
  3515. para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading2 ||
  3516. para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading3 ||
  3517. para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading4;
  3518. if (!isHeading)
  3519. {
  3520. // 非标题:强制统一设置为10.5pt,确保与非第一个内容项一致
  3521. run.Font.Size = 10.5;
  3522. }
  3523. // 标题保持原有大小,但字体名称已在上面的逻辑中统一
  3524. }
  3525. }
  3526. }
  3527. else
  3528. {
  3529. // 非第一个内容项:插入到目标段落位置
  3530. builder.InsertDocument(tempDoc, Aspose.Words.ImportFormatMode.KeepSourceFormatting);
  3531. // 统一字体格式
  3532. var appendedBody = doc.FirstSection.Body;
  3533. var allParagraphs = appendedBody.Paragraphs;
  3534. foreach (Aspose.Words.Paragraph para in allParagraphs)
  3535. {
  3536. // 跳过空段落(不删除)
  3537. if (string.IsNullOrWhiteSpace(para.GetText().Trim()))
  3538. {
  3539. continue;
  3540. }
  3541. // 统一段落格式
  3542. para.ParagraphFormat.SpaceBefore = 0;
  3543. if (para.ParagraphFormat.SpaceAfter > 6)
  3544. {
  3545. para.ParagraphFormat.SpaceAfter = 6;
  3546. }
  3547. // 统一字体格式
  3548. foreach (Aspose.Words.Run run in para.Runs)
  3549. {
  3550. // 统一字体名称
  3551. if (string.IsNullOrEmpty(run.Font.Name) ||
  3552. (!run.Font.Name.Contains("微软") && !run.Font.Name.Contains("Microsoft") &&
  3553. !run.Font.Name.Contains("Sim") && !run.Font.Name.Contains("宋体")))
  3554. {
  3555. run.Font.Name = "微软雅黑";
  3556. }
  3557. // 统一字体大小
  3558. bool isHeading = para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading1 ||
  3559. para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading2 ||
  3560. para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading3 ||
  3561. para.ParagraphFormat.StyleIdentifier == Aspose.Words.StyleIdentifier.Heading4;
  3562. if (!isHeading)
  3563. {
  3564. run.Font.Size = 10.5;
  3565. }
  3566. }
  3567. }
  3568. }
  3569. }
  3570. // 移动builder到文档末尾
  3571. builder.MoveToDocumentEnd();
  3572. }
  3573. // 如果不是最后一个内容项,添加三个段落作为间隔
  3574. if (i < contentItems.Count - 1)
  3575. {
  3576. for (int p = 0; p < 3; p++)
  3577. {
  3578. builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak);
  3579. targetParagraph = builder.CurrentParagraph;
  3580. }
  3581. }
  3582. }
  3583. else if (item.Type == WordContentType.Table)
  3584. {
  3585. // 处理表格内容
  3586. if (item.TableData != null && item.TableData.Count > 0)
  3587. {
  3588. // 确保builder在正确位置
  3589. if (i == 0)
  3590. {
  3591. // 第一个内容项,确保在文档开头
  3592. builder.MoveToDocumentStart();
  3593. // 清理开头的空段落
  3594. var tableBody = doc.FirstSection.Body;
  3595. while (tableBody.Paragraphs.Count > 0)
  3596. {
  3597. var firstPara = tableBody.Paragraphs[0];
  3598. if (string.IsNullOrWhiteSpace(firstPara.GetText().Trim()))
  3599. {
  3600. firstPara.Remove();
  3601. }
  3602. else
  3603. {
  3604. break;
  3605. }
  3606. }
  3607. builder.MoveToDocumentStart();
  3608. }
  3609. else
  3610. {
  3611. builder.MoveToDocumentEnd();
  3612. }
  3613. // 添加表格标题(如果有)
  3614. if (!string.IsNullOrWhiteSpace(item.TableTitle))
  3615. {
  3616. builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak);
  3617. builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak);
  3618. builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak);
  3619. // 强制设置为标题3样式
  3620. builder.ParagraphFormat.StyleIdentifier = Aspose.Words.StyleIdentifier.Heading3;
  3621. builder.ParagraphFormat.SpaceAfter = 6;
  3622. builder.ParagraphFormat.SpaceBefore = 0; // 表格标题前不添加间距
  3623. builder.Font.Size = 12;
  3624. builder.Font.Bold = true;
  3625. builder.Font.Name = "微软雅黑";
  3626. builder.Writeln(item.TableTitle);
  3627. // 恢复为正常段落样式
  3628. builder.ParagraphFormat.StyleIdentifier = Aspose.Words.StyleIdentifier.Normal;
  3629. builder.Font.Bold = false;
  3630. builder.Font.Size = 10.5;
  3631. builder.ParagraphFormat.SpaceAfter = 3; // 标题和表格之间的小间距
  3632. }
  3633. // 计算列数(取第一行的列数)
  3634. int columnCount = item.TableData[0]?.Count ?? 0;
  3635. if (columnCount == 0)
  3636. {
  3637. continue; // 跳过空表格
  3638. }
  3639. // 创建表格
  3640. Aspose.Words.Tables.Table table = builder.StartTable();
  3641. // 设置列宽(平均分配)- 在添加行之前设置
  3642. for (int colIndex = 0; colIndex < columnCount; colIndex++)
  3643. {
  3644. builder.CellFormat.PreferredWidth = Aspose.Words.Tables.PreferredWidth.FromPercent(100.0 / columnCount);
  3645. }
  3646. // 先添加表头(第一行),这样表格就不是空的了
  3647. if (item.TableData.Count > 0)
  3648. {
  3649. var headerRow = item.TableData[0];
  3650. foreach (var headerCell in headerRow)
  3651. {
  3652. builder.InsertCell();
  3653. builder.CellFormat.VerticalAlignment = Aspose.Words.Tables.CellVerticalAlignment.Center;
  3654. builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.FromArgb(240, 240, 240);
  3655. builder.ParagraphFormat.Alignment = Aspose.Words.ParagraphAlignment.Center;
  3656. builder.Font.Bold = true;
  3657. builder.Font.Size = 10.5;
  3658. builder.Font.Name = "微软雅黑";
  3659. builder.Font.Color = System.Drawing.Color.Black;
  3660. builder.Write(headerCell ?? "");
  3661. builder.Font.Bold = false;
  3662. }
  3663. builder.EndRow();
  3664. }
  3665. // 现在表格有行了,可以设置表格样式
  3666. table.StyleIdentifier = Aspose.Words.StyleIdentifier.LightGridAccent1;
  3667. table.AllowAutoFit = true;
  3668. table.PreferredWidth = Aspose.Words.Tables.PreferredWidth.FromPercent(100);
  3669. table.Alignment = Aspose.Words.Tables.TableAlignment.Center;
  3670. // 添加数据行
  3671. for (int rowIndex = 1; rowIndex < item.TableData.Count; rowIndex++)
  3672. {
  3673. var row = item.TableData[rowIndex];
  3674. // 确保列数一致
  3675. while (row.Count < columnCount)
  3676. {
  3677. row.Add("");
  3678. }
  3679. foreach (var cell in row.Take(columnCount))
  3680. {
  3681. builder.InsertCell();
  3682. builder.CellFormat.VerticalAlignment = Aspose.Words.Tables.CellVerticalAlignment.Center;
  3683. builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.White;
  3684. builder.ParagraphFormat.Alignment = Aspose.Words.ParagraphAlignment.Center;
  3685. builder.Font.Size = 10.5;
  3686. builder.Font.Name = "微软雅黑";
  3687. builder.Write(cell ?? "");
  3688. }
  3689. builder.EndRow();
  3690. }
  3691. builder.EndTable();
  3692. // 设置较小的段落间距
  3693. builder.ParagraphFormat.SpaceAfter = 6;
  3694. // 移动builder到文档末尾
  3695. builder.MoveToDocumentEnd();
  3696. }
  3697. // 如果不是最后一个内容项,添加三个段落作为间隔
  3698. if (i < contentItems.Count - 1)
  3699. {
  3700. for (int p = 0; p < 3; p++)
  3701. {
  3702. builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak);
  3703. targetParagraph = builder.CurrentParagraph;
  3704. }
  3705. }
  3706. }
  3707. }
  3708. // 只清理文档开头的空段落(第一个有内容的段落之前的空段落)
  3709. var body = doc.FirstSection.Body;
  3710. var paragraphs = body.Paragraphs;
  3711. while (paragraphs.Count > 0)
  3712. {
  3713. var firstPara = paragraphs[0];
  3714. var paraText = firstPara.GetText().Trim();
  3715. // 如果第一个段落是空的或只包含空白字符,删除它
  3716. if (string.IsNullOrWhiteSpace(paraText))
  3717. {
  3718. firstPara.Remove();
  3719. }
  3720. else
  3721. {
  3722. // 找到第一个有内容的段落,确保它没有前间距
  3723. firstPara.ParagraphFormat.SpaceBefore = 0;
  3724. break;
  3725. }
  3726. }
  3727. // 注意:不删除内容项之间用于间隔的空段落
  3728. // 遍历所有段落,确保字体设置正确,并统一段落间距(不删除空段落)
  3729. foreach (Aspose.Words.Paragraph paragraph in doc.GetChildNodes(Aspose.Words.NodeType.Paragraph, true))
  3730. {
  3731. // 跳过空段落(不删除,用于内容项之间的间隔)
  3732. if (string.IsNullOrWhiteSpace(paragraph.GetText().Trim()))
  3733. {
  3734. continue;
  3735. }
  3736. // 统一段落间距
  3737. if (paragraph.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading1 &&
  3738. paragraph.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading2 &&
  3739. paragraph.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading3 &&
  3740. paragraph.ParagraphFormat.StyleIdentifier != Aspose.Words.StyleIdentifier.Heading4)
  3741. {
  3742. // 普通段落保持较小的间距
  3743. if (paragraph.ParagraphFormat.SpaceAfter > 6)
  3744. {
  3745. paragraph.ParagraphFormat.SpaceAfter = 6;
  3746. }
  3747. // 移除段落前的间距
  3748. if (paragraph.ParagraphFormat.SpaceBefore > 0)
  3749. {
  3750. paragraph.ParagraphFormat.SpaceBefore = 0;
  3751. }
  3752. }
  3753. foreach (Aspose.Words.Run run in paragraph.Runs)
  3754. {
  3755. // 如果字体不是中文字体,设置为微软雅黑
  3756. if (string.IsNullOrEmpty(run.Font.Name) ||
  3757. (!run.Font.Name.Contains("微软") && !run.Font.Name.Contains("Microsoft") &&
  3758. !run.Font.Name.Contains("Sim") && !run.Font.Name.Contains("宋体")))
  3759. {
  3760. run.Font.Name = "微软雅黑";
  3761. }
  3762. }
  3763. }
  3764. // 保存文件
  3765. string saveFolder = $"{AppSettingsHelper.Get("WordBasePath")}PerformanceAnalysis";
  3766. if (!Directory.Exists(saveFolder))
  3767. {
  3768. Directory.CreateDirectory(saveFolder);
  3769. }
  3770. string outputFile = $"{fileName}_{DateTime.Now:yyyyMMddHHmmss}.pdf";
  3771. string filePath = Path.Combine(saveFolder, outputFile);
  3772. doc.Save(filePath, Aspose.Words.SaveFormat.Pdf);
  3773. // 返回访问URL
  3774. return $"{AppSettingsHelper.Get("WordBaseUrl")}Office/Word/PerformanceAnalysis/{outputFile}";
  3775. }
  3776. [HttpGet]
  3777. public async Task<IActionResult> AiPerformanceAnalysis_QueryAsync(int year, int month, int userId)
  3778. {
  3779. if (userId < 1)
  3780. {
  3781. return Ok(JsonView(false, "请传入有效的userId参数!"));
  3782. }
  3783. var data = new Pm_PerformanceAnalysis();
  3784. if (year < 1 && month < 1)
  3785. {
  3786. data = await _sqlSugar.Queryable<Pm_PerformanceAnalysis>()
  3787. .Where(x => x.UserId == userId && x.IsDel == 0)
  3788. .OrderByDescending(x => x.CreateTime)
  3789. .FirstAsync();
  3790. }
  3791. else
  3792. {
  3793. data = await _sqlSugar.Queryable<Pm_PerformanceAnalysis>()
  3794. .Where(x => x.UserId == userId && x.Year == year && x.Month == month && x.IsDel == 0)
  3795. .OrderByDescending(x => x.CreateTime)
  3796. .FirstAsync();
  3797. }
  3798. var result = JsonConvert.DeserializeObject<AiResponse>(data.JsonResult) ?? new AiResponse();
  3799. return Ok(JsonView(true, "操作成功!", new
  3800. {
  3801. data.JsonResult,
  3802. data.Year,
  3803. data.Month,
  3804. data.Id,
  3805. data.CreateTime,
  3806. data = result
  3807. }));
  3808. }
  3809. /// <summary>
  3810. /// Ai绩效分析
  3811. /// 列表查询基础数据
  3812. /// </summary>
  3813. /// <returns></returns>
  3814. [HttpGet]
  3815. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  3816. public async Task<IActionResult> AiPerformanceAnalysis_InitAsync()
  3817. {
  3818. var companyDatas = await _sqlSugar.Queryable<Sys_Company>().Where(x => x.IsDel == 0).Select(x => new { x.Id, x.CompanyName }).ToListAsync();
  3819. var jobPostDatas = await _sqlSugar.Queryable<Sys_JobPost>().Where(x => x.IsDel == 0).Select(x => new { x.Id, x.CompanyId, x.JobName }).ToListAsync();
  3820. var userDatas = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0).Select(x => new { x.Id, x.CompanyId, x.JobPostId, x.CnName }).ToListAsync();
  3821. companyDatas.Insert(0, new { Id = 0, CompanyName = "全部" });
  3822. jobPostDatas.Insert(0, new { Id = 0, CompanyId = 0, JobName = "全部" });
  3823. userDatas.Insert(0, new { Id = 0, CompanyId = 0, JobPostId = 0, CnName = "全部" });
  3824. return Ok(JsonView(true, "操作成功!", new
  3825. {
  3826. companyDatas,
  3827. jobPostDatas,
  3828. userDatas
  3829. }));
  3830. }
  3831. /// <summary>
  3832. /// Ai绩效分析
  3833. /// 用户列表
  3834. /// </summary>
  3835. /// <returns></returns>
  3836. [HttpPost]
  3837. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  3838. public async Task<IActionResult> AiPerformanceAnalysis_UserListAsync(AiPerformanceAnalysis_UserListDto dto)
  3839. {
  3840. if (dto.PageIndex < 1)
  3841. {
  3842. dto.PageIndex = 1;
  3843. }
  3844. if (dto.PageSize < 1 || dto.PageSize > 100)
  3845. {
  3846. dto.PageSize = 10;
  3847. }
  3848. RefAsync<int> total = 0;
  3849. var notidsJson = _sqlSugar.Queryable<Sys_SetData>().First(x => x.Id == 1463 && x.IsDel == 0)?.Remark;
  3850. var notids = new List<int>();
  3851. if (!string.IsNullOrWhiteSpace(notidsJson))
  3852. {
  3853. try
  3854. {
  3855. notids = JsonConvert.DeserializeObject<List<int>>(notidsJson) ?? new List<int>();
  3856. }
  3857. catch
  3858. { }
  3859. }
  3860. var query = _sqlSugar.Queryable<Sys_Users>()
  3861. .LeftJoin<Sys_Company>((u, c) => u.CompanyId == c.Id)
  3862. .LeftJoin<Sys_Department>((u, c, d) => u.DepId == d.Id)
  3863. .LeftJoin<Sys_JobPost>((u, c, d, jp) => u.JobPostId == jp.Id)
  3864. .Where((u, c, d, jp) => u.IsDel == 0 && !notids.Contains(u.Id))
  3865. .WhereIF(!string.IsNullOrEmpty(dto.ScreeningCriteria?.Trim()), (u, c, d, jp) =>
  3866. u.CnName.Contains(dto.ScreeningCriteria.Trim()) ||
  3867. c.CompanyName.Contains(dto.ScreeningCriteria.Trim()) ||
  3868. d.DepName.Contains(dto.ScreeningCriteria.Trim()) ||
  3869. jp.JobName.Contains(dto.ScreeningCriteria.Trim()) ||
  3870. SqlFunc.ToString(u.Id).Contains(dto.ScreeningCriteria.Trim()))
  3871. .OrderBy(u => u.CompanyId)
  3872. .OrderBy(u => u.DepId)
  3873. .OrderBy(u => u.JobPostId)
  3874. .OrderBy(u => u.Id);
  3875. var view = await query.Select((u, c, d, jp) => new AiPerformanceAnalysis_UserView()
  3876. {
  3877. RowNumber = SqlFunc.RowNumber("u.CompanyId ASC, u.JobPostId ASC, u.Id ASC"),
  3878. Id = u.Id,
  3879. UserName = u.CnName,
  3880. Sex = u.Sex == 0 ? "男" : u.Sex == 1 ? "女" : "未设置",
  3881. EDate = u.Edate.ToString("yyyy-MM-dd"),
  3882. CompanyId = u.CompanyId,
  3883. CompanyName = c.CompanyName,
  3884. DepId = u.DepId,
  3885. DepName = d.DepName,
  3886. JobPostId = u.JobPostId,
  3887. JobName = jp.JobName,
  3888. WorkYears = SqlFunc.DateDiff(DateType.Year, u.Edate, DateTime.Now),
  3889. HasPerformanceData = SqlFunc.Subqueryable<Pm_PerformanceAnalysis>()
  3890. .Where(pa => pa.UserId == u.Id && pa.Year == dto.Year && pa.Month == dto.Month && pa.IsDel == 0)
  3891. .Any()
  3892. })
  3893. .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
  3894. return Ok(JsonView(true, "操作成功!", view, total));
  3895. }
  3896. /// <summary>
  3897. /// Ai绩效分析
  3898. /// 团组统计
  3899. /// </summary>
  3900. /// <param name="userId">用户ID</param>
  3901. /// <param name="start">开始日期(eg:2025-01-01)</param>
  3902. /// <param name="end">结束日期(eg:2025-12-31)</param>
  3903. /// <returns></returns>
  3904. [HttpGet]
  3905. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  3906. public async Task<IActionResult> AiPerformanceAnalysis_GroupStatisticsAsync(int userId, DateTime start, DateTime end)
  3907. {
  3908. #region 参数验证
  3909. if (userId < 1)
  3910. {
  3911. return Ok(JsonView(false, "请传入有效UserId。"));
  3912. }
  3913. if (start > end)
  3914. {
  3915. return Ok(JsonView(false, "开始日期不能晚于结束日期。"));
  3916. }
  3917. #endregion
  3918. var parameters = new
  3919. {
  3920. UserId = userId,
  3921. StartDate = start.Date,
  3922. EndDate = end.Date.AddDays(1).AddSeconds(-1) // 包含结束日期的最后一秒
  3923. };
  3924. string sql = @"
  3925. SELECT
  3926. ROW_NUMBER() OVER (ORDER BY MonthNumber, CollectionDays) AS RowNumber,
  3927. Id,
  3928. TeamName,
  3929. ClientUnit,
  3930. ClientName,
  3931. VisitDate,
  3932. VisitPNumber,
  3933. JietuanOperator,
  3934. VisitEndDate,
  3935. MonthNumber,
  3936. GroupSales,
  3937. GroupPickupUser,
  3938. CollectionDays
  3939. FROM
  3940. (
  3941. SELECT
  3942. di.Id,
  3943. di.TeamName,
  3944. di.ClientUnit,
  3945. di.ClientName,
  3946. di.VisitDate,
  3947. di.VisitPNumber,
  3948. di.JietuanOperator,
  3949. di.VisitEndDate,
  3950. DATEPART(MONTH, di.VisitEndDate) AS MonthNumber,
  3951. (
  3952. SELECT CAST(COALESCE(SUM(ItemSumPrice * Rate), 0) AS DECIMAL(12, 2))
  3953. FROM Fin_ForeignReceivables
  3954. WHERE IsDel = 0
  3955. AND di.Id = Diid
  3956. AND AddingWay IN (0, 1, 2)
  3957. ) AS GroupSales,
  3958. u.CnName AS GroupPickupUser,
  3959. DATEADD(DAY, 7, di.VisitEndDate) AS CollectionDays
  3960. FROM Grp_DelegationInfo di WITH (NOLOCK)
  3961. LEFT JOIN Sys_Users u ON di.JietuanOperator = u.Id
  3962. WHERE di.Isdel = 0
  3963. AND di.IsBid = 0
  3964. AND di.JietuanOperator = @UserId
  3965. AND di.TeamName NOT LIKE '%投标%'
  3966. AND di.VisitDate BETWEEN @StartDate AND @EndDate
  3967. AND EXISTS (
  3968. SELECT 1
  3969. FROM Fin_ForeignReceivables fr
  3970. WHERE fr.IsDel = 0
  3971. AND fr.Diid = di.Id
  3972. AND fr.AddingWay IN (0, 1, 2)
  3973. )
  3974. ) Temp
  3975. ORDER BY MonthNumber, CollectionDays";
  3976. var groupData = await _sqlSugar.SqlQueryable<AiPerformanceAnalysis_GroupStatisticsView>(sql)
  3977. .AddParameters(parameters)
  3978. .ToListAsync();
  3979. return Ok(JsonView(true, "操作成功!", groupData, groupData?.Count ?? 0));
  3980. }
  3981. private class AiPerformanceAnalysis_GroupStatisticsView : MarketingSalesGroupList
  3982. {
  3983. /// <summary>
  3984. ///月份
  3985. /// </summary>
  3986. public int MonthNumber { get; set; }
  3987. }
  3988. /// <summary>
  3989. /// AI绩效分析用户列表视图模型
  3990. /// </summary>
  3991. private class AiPerformanceAnalysis_UserView
  3992. {
  3993. public int RowNumber { get; set; }
  3994. public int Id { get; set; }
  3995. public string UserName { get; set; }
  3996. public string Sex { get; set; }
  3997. public string EDate { get; set; }
  3998. public int CompanyId { get; set; }
  3999. public string CompanyName { get; set; }
  4000. public int DepId { get; set; }
  4001. public string DepName { get; set; }
  4002. public int JobPostId { get; set; }
  4003. public string JobName { get; set; }
  4004. public int WorkYears { get; set; }
  4005. /// <summary>
  4006. /// 是否存在绩效分析数据
  4007. /// </summary>
  4008. public bool HasPerformanceData { get; set; }
  4009. }
  4010. #endregion
  4011. }
  4012. }