UserController.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. using System;
  2. using System.Linq;
  3. using System.Threading.Tasks;
  4. using FMSAdmin.Data;
  5. using FMSAdmin.Models;
  6. using FMSAdmin.Entities;
  7. using FMSAdmin.Services;
  8. using Microsoft.AspNetCore.Mvc;
  9. using Microsoft.Extensions.Logging;
  10. using Microsoft.AspNetCore.Authorization;
  11. using Microsoft.EntityFrameworkCore;
  12. using FMSAdmin.Helpers;
  13. using System.Data;
  14. using System.IO;
  15. using OfficeOpenXml;
  16. using System.Security.Cryptography;
  17. using System.Text;
  18. using System.Net.Mail;
  19. using System.Net;
  20. namespace FMSAdmin.Controllers {
  21. [Authorize]
  22. [ApiController]
  23. [ApiVersion("1")]
  24. [Route("api/users")]
  25. public class UserController : Controller {
  26. private readonly ILogger<UserController> _logger;
  27. private readonly FMSContext _context;
  28. private readonly UserService _service;
  29. private readonly StorageHelper _storage;
  30. public UserController(
  31. ILogger<UserController> logger,
  32. FMSContext context,
  33. UserService service,
  34. StorageHelper storage
  35. ) {
  36. _logger = logger;
  37. _context = context;
  38. _service = service;
  39. _storage = storage;
  40. }
  41. /// <summary>
  42. /// 로그인
  43. /// </summary>
  44. [AllowAnonymous]
  45. [HttpPost("[action]")]
  46. public IActionResult Login([FromBody] LoginModel model) {
  47. _logger.LogInformation(model.Dump());
  48. var user = _service.Login(model);
  49. if (user == null)
  50. return BadRequest("아이디나 비밀번호가 잘못되었습니다.");
  51. if (user.EmploymentStatus == EmploymentStatus.퇴직)
  52. return BadRequest("로그인 할 수 없습니다.");
  53. return Ok(new {
  54. user.SiteId,
  55. user.UserId,
  56. user.UserGroupId,
  57. UserGroup = new {
  58. user.UserGroupId,
  59. user.Name,
  60. },
  61. user.Name,
  62. user.OfficePhoneNo,
  63. user.Email,
  64. user.PositionId,
  65. Position = new {
  66. user.CmPosition?.Name,
  67. },
  68. SiteName = user.CmSite?.Name,
  69. user.RefreshToken,
  70. user.AccessToken,
  71. user.IsAdmin,
  72. user.PasswordUpdatedDate,
  73. user.IsSysop,
  74. });
  75. }
  76. /// <summary>
  77. /// 패스워드 변경
  78. /// </summary>
  79. [AllowAnonymous]
  80. [HttpPost("[action]/{siteId}/{id}")]
  81. public IActionResult ChangePassword(int siteId, string id, [FromForm] string password, [FromForm] string auth) {
  82. _logger.LogInformation("password:" + password);
  83. _logger.LogInformation("auth:" + auth);
  84. // auth 체크는 나중에..
  85. _service.ChangePassword(siteId, id, password);
  86. return Ok(id);
  87. }
  88. /// <summary>
  89. /// 목록
  90. /// </summary>
  91. [HttpGet]
  92. public IActionResult List([FromQuery] PagingRequest req, [FromQuery] string str, int? type) {
  93. _logger.LogInformation(req.Dump());
  94. var query = _FilterAndSort(req, str);
  95. if (type != null) query = query.Where(x => x.EmploymentStatus == EmploymentStatus.재직);
  96. var list = _ListSelect(query);
  97. var paging = PagingList.Pagination(list, req.page, req.limit);
  98. return Ok(paging);
  99. }
  100. /// <summary>
  101. /// 엑셀파일 익스포트
  102. /// </summary>
  103. [HttpGet("excel")]
  104. public IActionResult ExportExcel([FromQuery] PagingRequest req, string filename) {
  105. req.page = 1;
  106. req.limit = 0;
  107. var query = _FilterAndSort(req, "");
  108. var list = _ListSelect(query);
  109. var stream = new MemoryStream();
  110. using (var package = new ExcelPackage(stream)) {
  111. var workSheet = package.Workbook.Worksheets.Add("Sheet1");
  112. workSheet.Cells.LoadFromCollection(list, true);
  113. if (req.columns != null && req.columns.Length > 0) {
  114. workSheet.AddStyle(req.columns, req.sort?.order?.ToLower() == "asc", filename);
  115. }
  116. package.Save();
  117. }
  118. stream.Position = 0;
  119. string excelName = $"excel-{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.xlsx";
  120. return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", excelName);
  121. }
  122. /// <summary>
  123. /// 상세조회
  124. /// </summary>
  125. [HttpGet("{id}")]
  126. public IActionResult GetByUserId(string id) {
  127. var emp = _service.GetByUserId(id);
  128. if (emp == null)
  129. return NotFound();
  130. return Ok(new {
  131. emp.SiteId,
  132. emp.UserId,
  133. emp.UserGroupId,
  134. emp.Name,
  135. emp.OfficePhoneNo,
  136. emp.Email,
  137. //UserGroupName = emp.CmUserGroup.Name,
  138. emp.AccessToken,
  139. });
  140. }
  141. /// <summary>
  142. /// 조회
  143. /// </summary>
  144. /// <param name="siteId"></param>
  145. /// <param name="id"></param>
  146. /// <returns></returns>
  147. [HttpGet("{siteId}/{id}")]
  148. public IActionResult Get(int siteId, string id) {
  149. var data = _service.Gets(id, siteId);
  150. if (data.Count() == 0) {
  151. return NotFound("사용자정보를 찾을 수 없습니다.");
  152. }
  153. var item = data.Select(x => new {
  154. x.UserGroupId,
  155. CmUserGroup = new {
  156. x.CmUserGroup.Name,
  157. },
  158. x.SiteId,
  159. CmSite = new {
  160. x.CmSite.Name,
  161. },
  162. x.UserId,
  163. x.Name,
  164. x.Passwd,
  165. x.OfficePhoneNo,
  166. x.Email,
  167. x.EmploymentStatus,
  168. EmploymentStatusName = x.EmploymentStatus.ToString(),
  169. x.EmploymentType,
  170. EmploymentTypeName = x.EmploymentType.ToString(),
  171. x.CompanyId,
  172. CmCompany = new {
  173. x.CmCompany.Name,
  174. },
  175. x.DepartmentId,
  176. CmDepartment = new {
  177. x.CmDepartment.Name,
  178. },
  179. x.PositionId,
  180. CmPosition = new {
  181. x.CmPosition.Name,
  182. },
  183. x.BusinessFieldId,
  184. CmBusinessField = new {
  185. x.CmBusinessField.Name,
  186. },
  187. x.MobilePhoneNo,
  188. x.EnterDate,
  189. x.RetireDate,
  190. x.Certificated,
  191. x.IsScheduleUser,
  192. x.IsSi,
  193. x.IsMobile,
  194. x.Comment,
  195. x.IsUse,
  196. x.IsAdmin,
  197. CmFile = _storage.ParseFile(x.CmFile),
  198. CmFile1 = _storage.ParseFile(x.CmFile1),
  199. });
  200. return Ok(item.First());
  201. }
  202. /// <summary>
  203. /// 수정
  204. /// </summary>
  205. [HttpPost("{siteId}/{id}")]
  206. public IActionResult Edit(int siteId, string id, [FromBody] CmUser emp) {
  207. _logger.LogInformation(emp.Dump());
  208. /*
  209. var currentUserId = int.Parse(User.Identity.Name);
  210. if (id != currentUserId)
  211. return Forbid();
  212. if (!User.IsInRole(Role.Admin))
  213. return Forbid();
  214. */
  215. if (id != emp.UserId) {
  216. return NotFound();
  217. }
  218. //ModelState.Remove("Password"); // 이 수정에서는 비밀번호 제외
  219. if (ModelState.IsValid) {
  220. try {
  221. _service.Edit(id, siteId, emp);
  222. } catch (ServiceException ex) {
  223. return BadRequest(ex.Message);
  224. }
  225. } else {
  226. foreach (var ms in ModelState.ToArray()) {
  227. _logger.LogInformation(ms.Key);
  228. }
  229. return BadRequest(ModelState);
  230. }
  231. return Ok();
  232. }
  233. /// <summary>
  234. /// 등록
  235. /// </summary>
  236. [HttpPut]
  237. public IActionResult Create([FromBody] CmUser emp) {
  238. // if (!User.IsInRole("Admin"))
  239. // return Forbid();
  240. if (ModelState.IsValid) {
  241. try {
  242. _service.Create(emp);
  243. } catch (ServiceException ex) {
  244. return BadRequest(ex.Message);
  245. }
  246. } else {
  247. return BadRequest(ModelState);
  248. }
  249. return Ok();
  250. }
  251. /// <summary>
  252. /// 삭제
  253. /// </summary>
  254. [HttpDelete("{siteId}/{id}")]
  255. public IActionResult Delete(int siteId, string id) {
  256. _service.Delete(id, siteId);
  257. return Ok();
  258. }
  259. // Role 제한 테스트
  260. [AllowAnonymous]
  261. [HttpGet("auth")]
  262. public IActionResult Auth() {
  263. return Ok();
  264. }
  265. // 검색 & 정렬 공통
  266. private IQueryable<CmUser> _FilterAndSort(PagingRequest req, string str) {
  267. var query = _service.GetAll();
  268. // 기본 Entity 검색
  269. query = query.Filter(req.conditions);
  270. // 추가 검색 예제
  271. /*
  272. if (req.conditions != null) {
  273. foreach (var cond in req.conditions) {
  274. switch ($"{cond.field}_{cond.op}") {
  275. case "fffff_eq":
  276. query = query.Where(x => x.Name == cond.value);
  277. break;
  278. }
  279. }
  280. }
  281. */
  282. //query = query.Where(x => x.IsUse == true);
  283. //아이콘트롤스 시스템 관리자 HIDDEN
  284. query = query.Where(x => x.IsSysop == false);
  285. if (req.siteId != null) {
  286. query = query.Where(x => x.SiteId == Util.ToInt(req.siteId));
  287. }
  288. if (req.siteIds != null && req.siteIds.Length > 0) {
  289. query = query.Where(x => req.siteIds.Contains(x.SiteId));
  290. }
  291. // 업무분야 필드가 있는경우만 추가
  292. if (req.businessFieldId != null) {
  293. query = query.Where(x => x.SiteId == req.siteId);
  294. query = query.Where(x => x.BusinessFieldId == req.businessFieldId || x.CmBusinessField.Name == "공통");
  295. }
  296. if (str != null) {
  297. query = query.Where(x => x.UserId.Contains(str) || x.Name.Contains(str));
  298. }
  299. // 추가 정렬 예제
  300. /*
  301. if (req.sort != null) {
  302. switch ($"{req.sort.field}_{req.sort.order}") {
  303. case "userId_asc":
  304. query = query.OrderBy(x => x.UserId);
  305. break;
  306. case "userId_desc":
  307. query = query.OrderByDescending(x => x.UserId);
  308. break;
  309. }
  310. }
  311. */
  312. // 고정정렬 (사옥 준공순)
  313. var sortQuery = query.OrderBy(x => x.CmSite.SortOrderNo);
  314. sortQuery = sortQuery.ThenByDescending(x => x.CmPosition.Position);
  315. // 추가 정렬
  316. sortQuery = sortQuery.ThenSort(req.sort);
  317. return sortQuery;
  318. }
  319. // 셀렉트 공통
  320. private dynamic _ListSelect(IQueryable<CmUser> query) {
  321. var list = query.Select(x => new {
  322. x.SiteId,
  323. SiteName = x.CmSite.Name,
  324. x.UserId,
  325. x.UserGroupId,
  326. x.Name,
  327. CmUserGroup = new {
  328. x.CmUserGroup.UserGroupId,
  329. x.CmUserGroup.Name
  330. },
  331. UserGroupName = x.CmUserGroup.Name,
  332. x.EmploymentStatus,
  333. EmploymentStatusName = x.EmploymentStatus.ToString(),
  334. x.OfficePhoneNo,
  335. x.MobilePhoneNo,
  336. x.Email,
  337. x.AccessToken,
  338. CmCompanyNm = new {
  339. x.CmCompany.Name
  340. },
  341. CompanyName = x.CmCompany.Name,
  342. CmDepartment = new {
  343. x.CmDepartment.Name
  344. },
  345. DepartmentName = x.CmDepartment.Name,
  346. Dept = x.CmCompany.Name + '/' + x.CmPosition.Name,
  347. PositionName = x.CmPosition.Name,
  348. BusinessFieldName = x.CmBusinessField.Name,
  349. x.EmploymentType,
  350. EmploymentTypeName = x.EmploymentType.ToString(),
  351. CmFile = _storage.ParseFile(x.CmFile)
  352. });
  353. return list;
  354. }
  355. /// <summary>
  356. /// 로그인 - 사이트 ID 조회
  357. /// </summary>
  358. [AllowAnonymous]
  359. [HttpGet("GetSite")]
  360. public IActionResult GetSite() {
  361. var site = _service.GetCmSites();
  362. if (site == null)
  363. return BadRequest("사이트 정보를 가져오지 못했습니다.");
  364. var list = site.Select(x => new {
  365. Id = x.SiteId,
  366. x.Name,
  367. });
  368. return Ok(list);
  369. }
  370. [AllowAnonymous]
  371. [HttpPost("[action]/{reqid}/{email}")]
  372. public IActionResult SendEmailAuthCode(string email, string reqid) {
  373. // 요청번호 reqid 무시. 안쓰고있음
  374. if (string.IsNullOrEmpty(email)) {
  375. return BadRequest("이메일을 입력해주세요.");
  376. }
  377. email = email.Trim();
  378. var query = _service.GetAll().Where(x => x.Email == email);
  379. if (query.Count() == 0) {
  380. return BadRequest("이메일과 일치하는 회원이 없습니다.");
  381. }
  382. if (query.Count() > 1) {
  383. return BadRequest("동일한 이메일이 여러개 존재합니다.");
  384. }
  385. string authCode = "" + new Random().Next(10000, 99999);
  386. _logger.LogInformation("authCode:" + authCode);
  387. var user = query.FirstOrDefault();
  388. // 이메일 발송
  389. using (var message = new MailMessage()) {
  390. message.To.Add(new MailAddress(email, user.Name));
  391. message.From = new MailAddress("hdc.icontrols.rnd1@gmail.com", "FMS");
  392. message.Subject = "FMS 비밀번호 찾기 인증번호";
  393. message.Body = $"<div style='width:800px; padding:30px; text-align:center;font-size:20pt;'>{user.Name}님의 아이디는 {user.UserId.Substring(0, user.UserId.Length - 2)}** 입니다.<br/><br/><div style='font-size:15pt'>보안을 위해 아래 인증번호를 입력하시고,<br/>비밀번호를 새로 설정해 주시기 바랍니다.</div><br/><strong>인증번호 : {authCode}</strong></div>";
  394. message.IsBodyHtml = true;
  395. var smtp = new SmtpClient {
  396. Host = "smtp.gmail.com",
  397. Port = 587,
  398. EnableSsl = true,
  399. DeliveryMethod = SmtpDeliveryMethod.Network,
  400. Credentials = new NetworkCredential("hdc.icontrols.rnd1@gmail.com", "pwrd12#$"),
  401. Timeout = 20000
  402. };
  403. smtp.Send(message);
  404. }
  405. // 검증용 코드 출력
  406. string code = null;
  407. using (SHA256 mySHA256 = SHA256.Create()) {
  408. code = Convert.ToBase64String(mySHA256.ComputeHash(Encoding.UTF8.GetBytes(email + authCode))).Replace("+", ".").Replace("/", "_").Replace("=", "-");
  409. }
  410. return Ok(code);
  411. }
  412. [AllowAnonymous]
  413. [HttpPost("[action]/{authCode}/{code}/{email}")]
  414. public IActionResult ValidAuthCode(string authCode, string code, string email) {
  415. string check = "";
  416. if (string.IsNullOrEmpty(email)) {
  417. return BadRequest("이메일을 입력해주세요.");
  418. }
  419. if (string.IsNullOrEmpty(authCode)) {
  420. return BadRequest("인증번호를 입력해주세요.");
  421. }
  422. var query = _service.GetAll().Where(x => x.Email == email);
  423. if (query.Count() == 0) {
  424. return BadRequest("이메일과 일치하는 회원이 없습니다.");
  425. }
  426. if (query.Count() > 1) {
  427. return BadRequest("동일한 이메일이 여러개 존재합니다.");
  428. }
  429. var member = query.FirstOrDefault();
  430. // 검증용 코드로 검증
  431. authCode = authCode.Trim();
  432. using (SHA256 mySHA256 = SHA256.Create()) {
  433. check = Convert.ToBase64String(mySHA256.ComputeHash(Encoding.UTF8.GetBytes(email + authCode))).Replace("+", ".").Replace("/", "_").Replace("=", "-");
  434. }
  435. if (code != check) {
  436. return BadRequest("인증번호가 일치하지 않습니다.");
  437. }
  438. // 검증완료 비밀번호 초기화
  439. _service.ChangePassword(member.SiteId, member.UserId, member.UserId, true);
  440. return Ok(new {
  441. SiteId = member.SiteId,
  442. UserId = member.UserId,
  443. });
  444. }
  445. }
  446. }