对讲机管理
parent
016e7c62c1
commit
352100f314
|
|
@ -34,21 +34,11 @@ namespace OpenAuth.App.ServiceApp.FmFireSiteManage
|
|||
{
|
||||
public class FmFireSiteApp : SqlSugarBaseApp<FmSiteInfo, SugarDbContext>
|
||||
{
|
||||
private ClientWebSocket _socket;
|
||||
private IConfiguration _configuration;
|
||||
IOptions<KikvisionConfig> _options;
|
||||
IOptions<KikvisionConfig2> _options2;
|
||||
IOptions<JPushClientConfig> _jpoptions;
|
||||
|
||||
public FmFireSiteApp(IConfiguration configuration, IOptions<KikvisionConfig> options, IOptions<KikvisionConfig2> options2,
|
||||
IOptions<JPushClientConfig> jpoptions, ISugarUnitOfWork<SugarDbContext> unitWork,
|
||||
public FmFireSiteApp( ISugarUnitOfWork<SugarDbContext> unitWork,
|
||||
ISimpleClient<FmSiteInfo> repository, IAuth auth) : base(unitWork, repository, auth)
|
||||
{
|
||||
_auth = auth;
|
||||
_options = options;
|
||||
_options2 = options2;
|
||||
_configuration = configuration;
|
||||
_jpoptions = jpoptions;
|
||||
}
|
||||
|
||||
#region 站点管理
|
||||
|
|
|
|||
|
|
@ -0,0 +1,301 @@
|
|||
using OpenAuth.App.BaseApp.Base;
|
||||
using OpenAuth.Repository.Domain.FireManagement;
|
||||
using OpenAuth.Repository;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenAuth.App.Interface;
|
||||
using SqlSugar;
|
||||
using NPOI.HSSF.UserModel;
|
||||
using NPOI.SS.UserModel;
|
||||
using NPOI.XSSF.UserModel;
|
||||
using OpenAuth.App.Response;
|
||||
using Infrastructure;
|
||||
using OpenAuth.App.ServiceApp.FmInterphoneApp.Response;
|
||||
using Infrastructure.Extensions;
|
||||
using DocumentFormat.OpenXml.Drawing;
|
||||
using OpenAuth.App.ServiceApp.FmFireSiteManage.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.App.ServiceApp.FmInterphoneApp.Request;
|
||||
|
||||
namespace OpenAuth.App.ServiceApp.FmInterphoneApp
|
||||
{
|
||||
public class FmInterPhoneApp : SqlSugarBaseApp<FmInterphonePoint, SugarDbContext>
|
||||
{
|
||||
public FmInterPhoneApp(ISugarUnitOfWork<SugarDbContext> unitWork, ISimpleClient<FmInterphonePoint> repository, IAuth auth) : base(unitWork, repository, auth)
|
||||
{
|
||||
_auth = auth;
|
||||
}
|
||||
|
||||
#region 对讲机信息
|
||||
public async Task<Response<PageInfo<List<InterphoneInfoResp>>>> GetInterphoneInfo(int pageIndex, int pageSize, string account, int isbinding)
|
||||
{
|
||||
using (var db = base.UnitWork.CreateContext())
|
||||
{
|
||||
RefAsync<int> totalNumber = 0;
|
||||
var info = await db.FmInterphonePoint.AsQueryable()
|
||||
.WhereIF(!string.IsNullOrEmpty(account), (a) => a.account.Contains(account))
|
||||
.WhereIF(isbinding == 1, (a) => a.username == "" || a.username == null)
|
||||
.WhereIF(isbinding == 2, (a) => a.username != "" && a.username != null)
|
||||
.Select((a) => new InterphoneInfoResp()
|
||||
{
|
||||
account = a.account.SelectAll(),
|
||||
})
|
||||
.ToPageListAsync(pageIndex, pageSize, totalNumber);
|
||||
return new Response<PageInfo<List<InterphoneInfoResp>>>
|
||||
{
|
||||
Result = new PageInfo<List<InterphoneInfoResp>> { Items = info, Total = totalNumber }
|
||||
};
|
||||
}
|
||||
}
|
||||
public async Task<Response<bool>> AddOrUpdateInterphoneInfo(FmInterphonePoint info)
|
||||
{
|
||||
using (var db = base.UnitWork.CreateContext())
|
||||
{
|
||||
|
||||
var inter = db.FmInterphonePoint.AsQueryable().First(r => r.account == info.account);
|
||||
if (inter == null)
|
||||
{
|
||||
info.time = DateTime.Now;
|
||||
await db.FmInterphonePoint.InsertAsync(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
await db.FmInterphonePoint.UpdateAsync(info);
|
||||
}
|
||||
|
||||
if (db.Commit())
|
||||
{
|
||||
return new Response<bool> { Result = true, Message = "操作成功" };
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Response<bool> { Result = false, Message = "操作失败" };
|
||||
}
|
||||
}
|
||||
}
|
||||
//删除
|
||||
public async Task<Response<bool>> DeleteInterphoneInfo(List<string> ids)
|
||||
{
|
||||
using (var db = base.UnitWork.CreateContext())
|
||||
{
|
||||
await db.FmInterphonePoint.DeleteAsync(u => ids.Contains(u.account));
|
||||
if (db.Commit())
|
||||
{
|
||||
return new Response<bool> { Result = true, Message = "删除成功" };
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Response<bool> { Result = false, Message = "删除失败" };
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 导入
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <returns></returns>
|
||||
public string ExportInterphoneInfo(AddOrUpdateBatchExportUsersReq req)
|
||||
{
|
||||
IWorkbook wk = null;
|
||||
var root = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string message = "success";
|
||||
string extension = System.IO.Path.GetExtension(root + req.file_path);
|
||||
try
|
||||
{
|
||||
FileStream fs = File.OpenRead(root + req.file_path);
|
||||
if (extension.Equals(".xls"))
|
||||
{
|
||||
//把xls文件中的数据写入wk中
|
||||
wk = new HSSFWorkbook(fs);
|
||||
}
|
||||
else
|
||||
{
|
||||
//把xlsx文件中的数据写入wk中
|
||||
wk = new XSSFWorkbook(fs);
|
||||
}
|
||||
|
||||
fs.Close();
|
||||
//读取当前表数据
|
||||
ISheet sheet = wk.GetSheetAt(0);
|
||||
|
||||
IRow row = sheet.GetRow(0); //读取当前行数据
|
||||
//LastRowNum 是当前表的总行数-1(注意)
|
||||
int offset = 0;
|
||||
var beginrow = sheet.GetRow(0); //读取第一行,如果名称不对 不能导入
|
||||
if (beginrow.GetCell(0).ToString().Trim() != "账号")
|
||||
{
|
||||
throw new Exception("请按照模板格式导入数据");
|
||||
}
|
||||
using (var db = base.UnitWork.CreateContext())
|
||||
{
|
||||
for (int i = 1; i <= sheet.LastRowNum; i++)
|
||||
{
|
||||
row = sheet.GetRow(i); //读取当前行数据
|
||||
if (row != null)
|
||||
{
|
||||
if (row.GetCell(0) == null) continue;
|
||||
string account = row.GetCell(0) == null ? null : row.GetCell(0).ToString().Trim();
|
||||
string password = row.GetCell(1) == null ? "" : row.GetCell(1).ToString().Trim();
|
||||
string username = row.GetCell(2) == null ? null : row.GetCell(2).ToString().Trim();
|
||||
string groupname = row.GetCell(3) == null ? null : row.GetCell(3).ToString().Trim();
|
||||
string phone = row.GetCell(4) == null ? null : row.GetCell(4).ToString().Trim();
|
||||
var info = db.FmInterphonePoint.AsQueryable().First(r => r.account == account);
|
||||
if (info == null)
|
||||
{
|
||||
FmInterphonePoint newinfo = new FmInterphonePoint();
|
||||
newinfo.account = account;
|
||||
newinfo.passwd = password;
|
||||
newinfo.username = username;
|
||||
newinfo.groupname = groupname;
|
||||
newinfo.phone = phone;
|
||||
newinfo.time = DateTime.Now;
|
||||
db.FmInterphonePoint.InsertAsync(newinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.passwd = password;
|
||||
info.username = username;
|
||||
info.groupname = groupname;
|
||||
info.phone = phone;
|
||||
db.FmInterphonePoint.UpdateAsync(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
message = e.Message;
|
||||
//只在Debug模式下才输出
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
//导出
|
||||
public Response<MemoryStream> ExportInfo()
|
||||
{
|
||||
Response<MemoryStream> response = new Response<MemoryStream>();
|
||||
try
|
||||
{
|
||||
HSSFWorkbook workbook = new HSSFWorkbook();
|
||||
#region 内容样式
|
||||
|
||||
IFont font1 = workbook.CreateFont(); //创建一个字体样式对象
|
||||
font1.FontName = "Microsoft YaHei"; //和excel里面的字体对应
|
||||
//font1.Boldweight = short.MaxValue;//字体加粗
|
||||
font1.FontHeightInPoints = 12; //字体大小
|
||||
ICellStyle style = workbook.CreateCellStyle(); //创建样式对象
|
||||
style.BorderBottom = BorderStyle.Thin;
|
||||
style.BorderLeft = BorderStyle.Thin;
|
||||
style.BorderRight = BorderStyle.Thin;
|
||||
style.BorderTop = BorderStyle.Thin;
|
||||
style.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
|
||||
style.VerticalAlignment = VerticalAlignment.Center;
|
||||
style.SetFont(font1); //将字体样式赋给样式对象
|
||||
style.WrapText = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 标题样式
|
||||
|
||||
IFont font = workbook.CreateFont(); //创建一个字体样式对象
|
||||
font.FontName = "Microsoft YaHei"; //和excel里面的字体对应
|
||||
font.Boldweight = (short)FontBoldWeight.Bold; //字体加粗
|
||||
font.FontHeightInPoints = 12; //字体大小
|
||||
ICellStyle style1 = workbook.CreateCellStyle(); //创建样式对象
|
||||
style1.BorderBottom = BorderStyle.Thin;
|
||||
style1.BorderLeft = BorderStyle.Thin;
|
||||
style1.BorderRight = BorderStyle.Thin;
|
||||
style1.BorderTop = BorderStyle.Thin;
|
||||
style1.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
|
||||
style1.VerticalAlignment = VerticalAlignment.Center;
|
||||
style1.SetFont(font); //将字体样式赋给样式对象
|
||||
|
||||
#endregion
|
||||
using (var db = base.UnitWork.CreateContext())
|
||||
{
|
||||
var list = db.FmInterphonePoint.AsQueryable()
|
||||
.Select((a) => new InterphoneInfoResp()
|
||||
{
|
||||
account = a.account.SelectAll(),
|
||||
})
|
||||
.ToList();
|
||||
|
||||
#region 创建表头
|
||||
int m = list.Count / 60000 + 1;
|
||||
for (int k = 0; k < m; k++)
|
||||
{
|
||||
ISheet sheet = workbook.CreateSheet("Sheet" + k.ToString());
|
||||
IRow rowHeader = sheet.CreateRow(0);
|
||||
rowHeader.Height = 20 * 30;
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
//ModuleColumn header = headers[i];
|
||||
rowHeader.CreateCell(i);
|
||||
rowHeader.Cells[i].CellStyle = style1;
|
||||
sheet.SetColumnWidth(i, 20 * 350);
|
||||
}
|
||||
rowHeader.Cells[0].SetCellValue("账号");//序号
|
||||
rowHeader.Cells[1].SetCellValue("密码");
|
||||
rowHeader.Cells[2].SetCellValue("用户姓名");
|
||||
rowHeader.Cells[3].SetCellValue("分组名称");
|
||||
rowHeader.Cells[4].SetCellValue("联系方式");
|
||||
rowHeader.Cells[5].SetCellValue("经度");
|
||||
rowHeader.Cells[6].SetCellValue("纬度");
|
||||
rowHeader.Cells[7].SetCellValue("校验码");
|
||||
rowHeader.Cells[8].SetCellValue("上传时间");
|
||||
|
||||
#endregion
|
||||
|
||||
#region 填充数据
|
||||
var val = (k + 1) * 60000;
|
||||
var num = 60000;
|
||||
if (val > list.Count)
|
||||
{
|
||||
num = list.Count - k * 60000;
|
||||
}
|
||||
for (int i = 0; i < num; i++) //循环数据
|
||||
{
|
||||
var item = list[k * 60000 + i]; //获取数据
|
||||
IRow dataRow = sheet.CreateRow(i + 1); //创建行
|
||||
for (int j = 0; j < 9; j++) //循环表头
|
||||
{
|
||||
//创建单元格
|
||||
dataRow.CreateCell(j);
|
||||
dataRow.Cells[j].CellStyle = style; //添加单元格样式
|
||||
}
|
||||
dataRow.Cells[0].SetCellValue(list[i].account == null ? "" : list[i].account);
|
||||
dataRow.Cells[1].SetCellValue(list[i].passwd == null ? "" : list[i].passwd);
|
||||
dataRow.Cells[2].SetCellValue(list[i].username == null ? "" : list[i].username);
|
||||
dataRow.Cells[3].SetCellValue(list[i].groupname == null ? "" : list[i].groupname);
|
||||
dataRow.Cells[4].SetCellValue(list[i].phone == null ? "" : list[i].phone);
|
||||
dataRow.Cells[5].SetCellValue(list[i].lat);
|
||||
dataRow.Cells[6].SetCellValue(list[i].lng);
|
||||
dataRow.Cells[7].SetCellValue(list[i].crc);
|
||||
dataRow.Cells[8].SetCellValue(list[i].time.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
response.Result = new MemoryStream();
|
||||
workbook.Write(response.Result);
|
||||
workbook = null;
|
||||
response.Result.Close();
|
||||
response.Result.Dispose();
|
||||
response.Code = 200;
|
||||
response.Message = "获取成功";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Code = 500;
|
||||
response.Message = ex.Message;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenAuth.App.ServiceApp.FmInterphoneApp.Request
|
||||
{
|
||||
public class AddOrUpdateBatchExportUsersReq
|
||||
{
|
||||
public string file_path { get; set; }
|
||||
|
||||
public string token { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenAuth.App.ServiceApp.FmInterphoneApp.Response
|
||||
{
|
||||
public class InterphoneInfoResp
|
||||
{
|
||||
public string account { get; set; }
|
||||
public string passwd { get; set; }
|
||||
public double lat { get; set; }
|
||||
public double lng { get; set; }
|
||||
public DateTime time { get; set; }
|
||||
public long crc { get; set; }
|
||||
/// <summary>
|
||||
/// 用户名称
|
||||
/// </summary>
|
||||
public string username { get; set; }
|
||||
/// <summary>
|
||||
/// 分组名称
|
||||
/// </summary>
|
||||
public string groupname { get; set; }
|
||||
/// <summary>
|
||||
/// 联系方式
|
||||
/// </summary>
|
||||
public string phone { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenAuth.Repository.Domain.FireManagement
|
||||
{
|
||||
[SugarTable("fm_interphonepoint")]
|
||||
public class FmInterphonePoint
|
||||
{
|
||||
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public string account { get; set; }
|
||||
public string passwd { get; set; }
|
||||
public double lat { get; set; }
|
||||
public double lng { get; set; }
|
||||
public DateTime time { get; set; }
|
||||
public long crc { get; set; }
|
||||
/// <summary>
|
||||
/// 用户名称
|
||||
/// </summary>
|
||||
public string username { get; set; }
|
||||
/// <summary>
|
||||
/// 分组名称
|
||||
/// </summary>
|
||||
public string groupname { get; set; }
|
||||
/// <summary>
|
||||
/// 联系方式
|
||||
/// </summary>
|
||||
public string phone { get; set; }
|
||||
/// <summary>
|
||||
/// 是否置顶
|
||||
/// </summary>
|
||||
public int top { get; set; }
|
||||
/// <summary>
|
||||
/// 是否是联系人
|
||||
/// </summary>
|
||||
public int iscontacts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 厂商(0厂商一,1科立讯)
|
||||
/// </summary>
|
||||
public int manufacture { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -86,6 +86,7 @@ namespace OpenAuth.Repository
|
|||
public SugarRepositiry<FmCamera> FmCamera { get; set; }
|
||||
public SugarRepositiry<FmCamera_yjj> FmCamera_yjj { get; set; }
|
||||
public SugarRepositiry<FmPreventionplan> FmPreventionplan { get; set; }
|
||||
public SugarRepositiry<FmInterphonePoint> FmInterphonePoint { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
using Infrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.App.ServiceApp.FmFireSiteManage;
|
||||
using OpenAuth.App.ServiceApp.FmInterphoneApp;
|
||||
using OpenAuth.App.ServiceApp.FmInterphoneApp.Request;
|
||||
using OpenAuth.App.ServiceApp.FmInterphoneApp.Response;
|
||||
using OpenAuth.Repository.Domain.FireManagement;
|
||||
|
||||
namespace OpenAuth.WebApi.Controllers.ServiceControllers.FireManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// 对讲机管理模块
|
||||
/// </summary>
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class FmInterPhoneController : ControllerBase
|
||||
{
|
||||
private readonly FmInterPhoneApp _app;
|
||||
|
||||
public FmInterPhoneController(FmInterPhoneApp app)
|
||||
{
|
||||
_app = app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对讲机信息
|
||||
/// </summary>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="account">唯一编码</param>
|
||||
/// <param name="isbinding">是否绑定</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public async Task<Response<PageInfo<List<InterphoneInfoResp>>>> GetInterphoneInfo(int pageIndex, int pageSize, string account, int isbinding)
|
||||
{
|
||||
Response<PageInfo<List<InterphoneInfoResp>>> response = new Response<PageInfo<List<InterphoneInfoResp>>>();
|
||||
try
|
||||
{
|
||||
return await _app.GetInterphoneInfo(pageIndex, pageSize, account,isbinding);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Code = 500;
|
||||
response.Message = ex.InnerException?.Message ?? ex.Message;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加或者更新对讲机
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
public async Task<Response<bool>> AddOrUpdateInterphoneInfo(FmInterphonePoint info)
|
||||
{
|
||||
Response<bool> response = new();
|
||||
try
|
||||
{
|
||||
return await _app.AddOrUpdateInterphoneInfo(info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Code = 500;
|
||||
response.Message = ex.InnerException?.Message ?? ex.Message;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除对讲机
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
public async Task<Response<bool>> DeleteInterphoneInfo(List<string> ids)
|
||||
{
|
||||
Response<bool> response = new();
|
||||
try
|
||||
{
|
||||
return await _app.DeleteInterphoneInfo(ids);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Code = 500;
|
||||
response.Message = ex.InnerException?.Message ?? ex.Message;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导入数据
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
public Response<bool> ExportInterphoneInfo(AddOrUpdateBatchExportUsersReq req)
|
||||
{
|
||||
Response<bool> response = new Response<bool>();
|
||||
try
|
||||
{
|
||||
response.Message = _app.ExportInterphoneInfo(req);
|
||||
response.Result = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Code = 500;
|
||||
response.Message = ex.InnerException?.Message ?? ex.Message;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public IActionResult ExportInfo()
|
||||
{
|
||||
var res = new Response();
|
||||
var excelRes = _app.ExportInfo();
|
||||
if (excelRes.Code == 200)
|
||||
{
|
||||
return File(excelRes.Result.ToArray(),
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"对讲机数据" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xls");
|
||||
}
|
||||
else
|
||||
{
|
||||
res.Code = excelRes.Code;
|
||||
res.Message = "导出失败";
|
||||
}
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue