odf_new/server/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs
zpc 827d7a4367
All checks were successful
continuous-integration/drone/push Build is passing
feat(odf): Add marker pole management and audit logging for v1.2.0
- Add marker pole CRUD functionality with database tables (odf_marker_poles, odf_marker_pole_images)
- Implement marker pole API endpoints and service layer with data isolation by department
- Add UniApp pages for marker pole list, detail, and creation workflows
- Add Vue management backend pages for marker pole and audit log management
- Implement audit logging system via ActionFilter to track all business entity modifications
- Extend search API to include marker poles in results alongside cables and faults
- Add OdfAuditLogsController and service for querying audit trail data
- Update optical box detail page with left-right frame color scheme (green-orange)
- Add cable type page as entry point for marker poles and fault lists
- Create database migration scripts for v1.2.0 schema and permissions
- Add DeptDataScopeHelper for department-based data access control
- Update MCP settings to disable SQL Server connection and fix formatting
- Add marker pole service integration in UniApp with COS image upload support
2026-04-18 22:50:15 +08:00

431 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Mvc;
using ZR.Model.Models;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.System
{
/// <summary>
/// 登录
/// </summary>
[ApiExplorerSettings(GroupName = "sys")]
public class SysLoginController : BaseController
{
private readonly ISysUserService sysUserService;
private readonly ISysMenuService sysMenuService;
private readonly ISysLoginService sysLoginService;
private readonly ISysPermissionService permissionService;
private readonly ICaptcha SecurityCodeHelper;
private readonly ISysConfigService sysConfigService;
private readonly ISysRoleService roleService;
private readonly ISmsCodeLogService smsCodeLogService;
private readonly ISysDeptService deptService;
public SysLoginController(
ISysMenuService sysMenuService,
ISysUserService sysUserService,
ISysLoginService sysLoginService,
ISysPermissionService permissionService,
ISysConfigService configService,
ISysRoleService sysRoleService,
ISmsCodeLogService smsCodeLogService,
ISysDeptService deptService,
ICaptcha captcha)
{
SecurityCodeHelper = captcha;
this.sysMenuService = sysMenuService;
this.sysUserService = sysUserService;
this.sysLoginService = sysLoginService;
this.permissionService = permissionService;
this.sysConfigService = configService;
this.smsCodeLogService = smsCodeLogService;
this.deptService = deptService;
roleService = sysRoleService;
}
/// <summary>
/// 登录
/// </summary>
/// <param name="loginBody">登录对象</param>
/// <returns></returns>
[Route("login")]
[HttpPost]
[Log(Title = "登录")]
[AllowAnonymous]
public IActionResult Login([FromBody] LoginBodyDto loginBody)
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(loginBody.Uuid, loginBody.Code))
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
sysLoginService.CheckLockUser(loginBody.Username);
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
// 分公司账号登录管理后台限制:非超级管理员的分公司账号必须拥有「分公司管理员」角色
if (!user.IsAdmin && !roles.Any(r => r.RoleKey == GlobalConstant.AdminRole))
{
var dept = deptService.GetFirst(d => d.DeptId == user.DeptId);
if (dept != null)
{
// 判断是否为分公司级别Ancestors 中包含两级以上(如 "0,100" 表示分公司)
var ancestors = dept.Ancestors?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
bool isBranchCompany = ancestors.Length >= 2;
if (isBranchCompany && !roles.Any(r => r.RoleKey == "branch_admin"))
{
throw new CustomException(ResultCode.LOGIN_ERROR, "该账号无权登录管理后台,请联系管理员分配「分公司管理员」角色", false);
}
}
}
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(new SysUserDto() { UserId = user.UserId });
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>())
{
TenantId = loginBody.TenantId,
Permissions = permissions,
};
//CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
}
/// <summary>
/// 登录
/// </summary>
/// <param name="loginBody">登录对象</param>
/// <returns></returns>
[Route("appLogin")]
[HttpPost]
[Log(Title = "登录")]
[AllowAnonymous]
public IActionResult AppLogin([FromBody] LoginBodyDto loginBody)
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
sysLoginService.CheckLockUser(loginBody.Username);
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(new SysUserDto() { UserId = user.UserId });
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>())
{
TenantId = loginBody.TenantId,
Permissions = permissions,
};
//CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
var jwt = JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser));
return SUCCESS(new { jwt, user.UserId, user.UserName, permissions });
}
//
/// <summary>
/// 注销
/// </summary>
/// <returns></returns>
[Log(Title = "注销")]
[HttpPost("logout")]
[AllowAnonymous]
public IActionResult LogOut()
{
var userid = HttpContext.GetUId();
var name = HttpContext.GetName();
CacheService.RemoveUserPerms(GlobalConstant.UserPermKEY + userid);
return SUCCESS(new { name, id = userid });
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <returns></returns>
[HttpGet("getInfo")]
public IActionResult GetUserInfo()
{
long userId = HttpContext.GetUId();
var user = sysUserService.SelectUserById(userId);
//前端校验按钮权限使用
//角色集合 eg: admin,yunying,common
List<string> roles = permissionService.GetRolePermission(user);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)];
user.Password = string.Empty;
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + userId, permissions);
return SUCCESS(new { user = user.Adapt<SysUserDto>(), roles, permissions });
}
/// <summary>
/// 获取路由信息
/// </summary>
/// <returns></returns>
[HttpGet("getRouters")]
public IActionResult GetRouters()
{
long uid = HttpContext.GetUId();
var menus = sysMenuService.SelectMenuTreeByUserId(uid);
return SUCCESS(sysMenuService.BuildMenus(menus));
}
/// <summary>
/// 获取路由信息
/// </summary>
/// <returns></returns>
[HttpGet("getAppRouters")]
public IActionResult GetAppRouters(int v = 0)
{
long uid = HttpContext.GetUId();
var perms = permissionService.GetMenuPermission(new SysUserDto() { UserId = uid });
return SUCCESS(sysMenuService.GetAppMenus(perms, v));
}
/// <summary>
/// 生成图片验证码
/// </summary>
/// <returns></returns>
[HttpGet("captchaImage")]
[AllowAnonymous]
public IActionResult CaptchaImage()
{
string uuid = Guid.NewGuid().ToString().Replace("-", "");
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
var captchaOff = sysConfig?.ConfigValue ?? "0";
var info = SecurityCodeHelper.Generate(uuid, 60);
var obj = new { captchaOff, uuid, img = info.Base64 };// File(stream, "image/png")
return SUCCESS(obj);
}
/// <summary>
/// 注册
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/register")]
[AllowAnonymous]
[Log(Title = "注册", BusinessType = BusinessType.INSERT)]
public IActionResult Register([FromBody] RegisterDto dto)
{
SysConfig config = sysConfigService.GetSysConfigByKey("sys.account.register");
if (config?.ConfigValue != "true")
{
return ToResponse(ResultCode.CUSTOM_ERROR, "当前系统没有开启注册功能!");
}
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(dto.Uuid, dto.Code))
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
dto.UserIP = HttpContext.GetClientUserIp();
SysUser user = sysUserService.Register(dto);
if (user.UserId > 0)
{
return SUCCESS(user);
}
return ToResponse(ResultCode.CUSTOM_ERROR, "注册失败,请联系管理员");
}
#region
/// <summary>
/// 生成二维码
/// </summary>
/// <param name="uuid"></param>
/// <param name="deviceId"></param>
/// <returns></returns>
[HttpGet("/GenerateQrcode")]
[AllowAnonymous]
public IActionResult GenerateQrcode(string uuid, string deviceId)
{
var state = Guid.NewGuid().ToString();
var dict = new Dictionary<string, object>
{
{ "state", state }
};
CacheService.SetScanLogin(uuid, dict);
return SUCCESS(new
{
status = 1,
state,
uuid,
codeContent = new { uuid, deviceId }// "https://qm.qq.com/cgi-bin/qm/qr?k=kgt4HsckdljU0VM-0kxND6d_igmfuPlL&authKey=r55YUbruiKQ5iwC/folG7KLCmZ++Y4rQVgNlvLbUniUMkbk24Y9+zNuOmOnjAjRc&noverify=0"
});
}
/// <summary>
/// 轮询判断扫码状态
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/VerifyScan")]
[AllowAnonymous]
public IActionResult VerifyScan([FromBody] ScanDto dto)
{
int status = -1;
object token = string.Empty;
if (CacheService.GetScanLogin(dto.Uuid) is Dictionary<string, object> str)
{
status = 0;
str.TryGetValue("token", out token);
if (str.ContainsKey("status") && (string)str.GetValueOrDefault("status") == "success")
{
status = 2;//扫码成功
CacheService.RemoveScanLogin(dto.Uuid);
}
}
return SUCCESS(new { status, token });
}
/// <summary>
/// 移动端扫码登录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/ScanLogin")]
[Log(Title = "扫码登录")]
public IActionResult ScanLogin([FromBody] ScanDto dto)
{
if (dto == null) { return ToResponse(ResultCode.CUSTOM_ERROR, "扫码失败"); }
var name = App.HttpContext.GetName();
sysLoginService.CheckLockUser(name);
TokenModel tokenModel = JwtUtil.GetLoginUser(HttpContext);
if (CacheService.GetScanLogin(dto.Uuid) is not null)
{
Dictionary<string, object> dict = new() { };
dict.Add("status", "success");
dict.Add("token", JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(tokenModel)));
CacheService.SetScanLogin(dto.Uuid, dict);
return SUCCESS(1);
}
return ToResponse(ResultCode.FAIL, "二维码已失效");
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/checkMobile")]
[Log(Title = "发送短息", BusinessType = BusinessType.INSERT)]
[AllowAnonymous]
public IActionResult CheckMobile([FromBody] PhoneLoginDto dto)
{
dto.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
var uid = HttpContext.GetUId();
//SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
//if (!SecurityCodeHelper.Validate(dto.Uuid, dto.Code, false))
//{
// return ToResponse(ResultCode.CUSTOM_ERROR, "验证码错误");
//}
if (dto.SendType == 0)
{
var info = sysUserService.GetFirst(f => f.Phonenumber == dto.PhoneNum) ?? throw new CustomException(ResultCode.CUSTOM_ERROR, "该手机号不存在", false);
uid = info.UserId;
}
if (dto.SendType == 1)
{
if (sysUserService.CheckPhoneBind(dto.PhoneNum).Count > 0)
{
return ToResponse(ResultCode.CUSTOM_ERROR, "手机号已绑定其他账号");
}
}
string location = HttpContextExtension.GetIpInfo(dto.LoginIP);
smsCodeLogService.AddSmscodeLog(new SmsCodeLog()
{
Userid = uid,
PhoneNum = dto.PhoneNum.ParseToLong(),
SendType = dto.SendType,
UserIP = dto.LoginIP,
Location = location,
});
return SUCCESS(1);
}
/// <summary>
/// 手机号登录
/// </summary>
/// <param name="loginBody">登录对象</param>
/// <returns></returns>
[Route("PhoneLogin")]
[HttpPost]
[Log(Title = "手机号登录")]
[AllowAnonymous]
public IActionResult PhoneLogin([FromBody] PhoneLoginDto loginBody)
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
if (!CacheService.CheckPhoneCode(loginBody.PhoneNum, loginBody.PhoneCode))
{
return ToResponse(ResultCode.CUSTOM_ERROR, "短信验证码错误");
}
var info = sysUserService.GetFirst(f => f.Phonenumber == loginBody.PhoneNum) ?? throw new CustomException(ResultCode.CUSTOM_ERROR, "该手机号不存在", false);
var infoModel = info.Adapt<SysUserDto>();
sysLoginService.CheckLockUser(info.UserName);
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
var user = sysLoginService.PhoneLogin(loginBody, new SysLogininfor() { LoginLocation = location }, infoModel);
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>())
{
TenantId = loginBody.TenantId,
Permissions = permissions,
};
//CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
}
/// <summary>
/// 手机号绑定
/// </summary>
/// <param name="loginBody"></param>
/// <returns></returns>
[Route("/PhoneBind")]
[HttpPost]
[Log(Title = "手机号绑定")]
[AllowAnonymous]
public IActionResult PhoneBind([FromBody] PhoneLoginDto loginBody)
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
var uid = HttpContext.GetUId();
if (!CacheService.CheckPhoneCode(loginBody.PhoneNum, loginBody.PhoneCode))
{
return ToResponse(ResultCode.CUSTOM_ERROR, "短信验证码错误");
}
var result = sysUserService.ChangePhoneNum(uid, loginBody.PhoneNum);
return SUCCESS(result);
}
}
}