PersonnelModuleController.cs 197 KB

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