fix: 🐛 ip归属地查询接口地址更新 (#168)

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-08-02 09:26:37 +08:00 committed by GitHub
parent e00c30c961
commit 4733adede5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 60 additions and 63 deletions

View File

@ -30,6 +30,18 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
set => Rpo.DbContextOptions.EnableCascadeSave = value; set => Rpo.DbContextOptions.EnableCascadeSave = value;
} }
/// <summary>
/// 导出实体
/// </summary>
protected async Task<IActionResult> ExportAsync<TQuery, TExport>( //
Func<QueryReq<TQuery>, ISelectGrouping<TEntity, TEntity>> selector, QueryReq<TQuery> query, string fileName
, Expression<Func<ISelectGroupingAggregate<TEntity, TEntity>, object>> listExp)
where TQuery : DataAbstraction, new()
{
var list = await selector(query).Take(Numbers.MAX_LIMIT_EXPORT).ToListAsync(listExp).ConfigureAwait(false);
return await GetExportFileStreamAsync<TExport>(fileName, list).ConfigureAwait(false);
}
/// <summary> /// <summary>
/// 导出实体 /// 导出实体
/// </summary> /// </summary>
@ -48,26 +60,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
? await select.ToListAsync().ConfigureAwait(false) ? await select.ToListAsync().ConfigureAwait(false)
: await select.ToListAsync(listExp).ConfigureAwait(false); : await select.ToListAsync(listExp).ConfigureAwait(false);
var listTyped = list.Adapt<List<TExport>>(); return await GetExportFileStreamAsync<TExport>(fileName, list).ConfigureAwait(false);
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteHeader<TExport>();
await csv.NextRecordAsync().ConfigureAwait(false);
foreach (var item in listTyped) {
csv.WriteRecord(item);
await csv.NextRecordAsync().ConfigureAwait(false);
}
await csv.FlushAsync().ConfigureAwait(false);
_ = stream.Seek(0, SeekOrigin.Begin);
App.HttpContext.Response.Headers.ContentDisposition
= new ContentDispositionHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_ATTACHMENT) {
FileNameStar = $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv"
}.ToString();
return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM);
} }
/// <summary> /// <summary>
@ -117,6 +110,31 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
} }
#endif #endif
private static async Task<IActionResult> GetExportFileStreamAsync<TExport>(string fileName, object list)
{
var listTyped = list.Adapt<List<TExport>>();
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteHeader<TExport>();
await csv.NextRecordAsync().ConfigureAwait(false);
foreach (var item in listTyped) {
csv.WriteRecord(item);
await csv.NextRecordAsync().ConfigureAwait(false);
}
await csv.FlushAsync().ConfigureAwait(false);
_ = stream.Seek(0, SeekOrigin.Begin);
App.HttpContext.Response.Headers.ContentDisposition
= new ContentDispositionHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_ATTACHMENT) {
FileNameStar = $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv"
}.ToString();
return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM);
}
private IUpdate<TEntity> BuildUpdate( // private IUpdate<TEntity> BuildUpdate( //
TEntity entity // TEntity entity //
, IEnumerable<string> includeFields // , IEnumerable<string> includeFields //

View File

@ -19,6 +19,8 @@ public record CreateLoginLogReq : Sys_LoginLog, IRegister
{ {
var body = s.Detail.ResponseBody.ToObject<RestfulInfo<LoginRsp>>(); var body = s.Detail.ResponseBody.ToObject<RestfulInfo<LoginRsp>>();
ContextUserToken userToken = null; ContextUserToken userToken = null;
// ReSharper disable once InvertIf
if (body.Data?.AccessToken != null) { if (body.Data?.AccessToken != null) {
try { try {
userToken = ContextUserToken.Create(body.Data.AccessToken); userToken = ContextUserToken.Create(body.Data.AccessToken);

View File

@ -10,7 +10,7 @@
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"ace-builds": "^1.35.2", "ace-builds": "^1.35.4",
"aieditor": "^1.0.13", "aieditor": "^1.0.13",
"axios": "^1.7.2", "axios": "^1.7.2",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
@ -18,7 +18,7 @@
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"element-plus": "^2.7.7", "element-plus": "^2.7.8",
"json-bigint": "^1.0.0", "json-bigint": "^1.0.0",
"json5-to-table": "^0.1.8", "json5-to-table": "^0.1.8",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
@ -28,7 +28,7 @@
"qrcodejs2": "^0.0.2", "qrcodejs2": "^0.0.2",
"sortablejs": "^1.15.2", "sortablejs": "^1.15.2",
"vkbeautify": "^0.99.3", "vkbeautify": "^0.99.3",
"vue": "^3.4.31", "vue": "^3.4.34",
"vue-i18n": "^9.13.1", "vue-i18n": "^9.13.1",
"vue-router": "^4.4.0", "vue-router": "^4.4.0",
"vue3-ace-editor": "^2.2.4", "vue3-ace-editor": "^2.2.4",
@ -37,12 +37,12 @@
"vuex": "^4.1.0" "vuex": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.1.1",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-organize-attributes": "^1.0.0", "prettier-plugin-organize-attributes": "^1.0.0",
"sass": "^1.77.8", "sass": "^1.77.8",
"terser": "^5.31.3", "terser": "^5.31.3",
"vite": "^5.3.4" "vite": "^5.3.5"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@ -1,30 +0,0 @@
<template>
<p>{{ ip ?? '-' }}</p>
<p style="overflow: hidden">{{ region ?? '-' }}</p>
</template>
<script>
import http from '@/utils/request'
export default {
emits: [],
props: ['ip'],
data() {
return {
region: null,
}
},
mounted() {},
created() {
if (this.ip) {
this.region = '...'
http.get(`http://ip.line92.xyz/?ip=${this.ip}`).then((x) => {
this.region = x.region
})
}
},
components: {},
computed: {},
methods: {},
}
</script>
<style scoped></style>

View File

@ -2,7 +2,7 @@
<form @keyup.enter="search" @submit.prevent="search" class="right-panel-search"> <form @keyup.enter="search" @submit.prevent="search" class="right-panel-search">
<el-date-picker <el-date-picker
v-if="hasDate" v-if="hasDate"
v-model="form.dy.createdTime" v-model="form.dy[this.dateField]"
:end-placeholder="$t('结束日期')" :end-placeholder="$t('结束日期')"
:format="dateFormat" :format="dateFormat"
:range-separator="$t('至')" :range-separator="$t('至')"
@ -83,6 +83,7 @@ import vkbeautify from 'vkbeautify/index'
export default { export default {
emits: ['search', 'reset'], emits: ['search', 'reset'],
props: { props: {
dateField: { type: String, default: 'createdTime' },
hasDate: { type: Boolean, default: true }, hasDate: { type: Boolean, default: true },
dateType: { type: String, default: 'daterange' }, dateType: { type: String, default: 'daterange' },
dateFormat: { type: String, default: 'YYYY-MM-DD' }, dateFormat: { type: String, default: 'YYYY-MM-DD' },
@ -114,8 +115,8 @@ export default {
text: this.$t('后退一日'), text: this.$t('后退一日'),
value: () => { value: () => {
try { try {
const start = new Date(new Date(this.form.dy.createdTime[0]) - 3600 * 1000 * 24) const start = new Date(new Date(this.form.dy[this.dateField][0]) - 3600 * 1000 * 24)
const end = new Date(new Date(this.form.dy.createdTime[1]) - 3600 * 1000 * 24) const end = new Date(new Date(this.form.dy[this.dateField][1]) - 3600 * 1000 * 24)
return [start, end] return [start, end]
} catch {} } catch {}
}, },
@ -356,8 +357,8 @@ export default {
text: this.$t('后退一时'), text: this.$t('后退一时'),
value: () => { value: () => {
try { try {
const start = new Date(new Date(this.form.dy.createdTime[0]) - 3600 * 1000) const start = new Date(new Date(this.form.dy[this.dateField][0]) - 3600 * 1000)
const end = new Date(new Date(this.form.dy.createdTime[1]) - 3600 * 1000) const end = new Date(new Date(this.form.dy[this.dateField][1]) - 3600 * 1000)
return [start, end] return [start, end]
} catch {} } catch {}
}, },

View File

@ -24,7 +24,12 @@
</div> </div>
<div class="sc-statistic-content"> <div class="sc-statistic-content">
<span v-if="prefix" class="sc-statistic-content-prefix">{{ prefix }}</span> <span v-if="prefix" class="sc-statistic-content-prefix">{{ prefix }}</span>
<span class="sc-statistic-content-value">{{ cmtValue }}</span> <span class="sc-statistic-content-value">
<slot v-if="$slots.content" name="content"></slot>
<template v-else>
{{ cmtValue }}
</template>
</span>
<span v-if="suffix" class="sc-statistic-content-suffix">{{ suffix }}</span> <span v-if="suffix" class="sc-statistic-content-suffix">{{ suffix }}</span>
</div> </div>
<div v-if="description || $slots.default" class="sc-statistic-description"> <div v-if="description || $slots.default" class="sc-statistic-description">

View File

@ -15,6 +15,7 @@
label: 'name', label: 'name',
}" }"
@node-click="click" @node-click="click"
default-expand-all
node-key="id" node-key="id"
ref="dic"> ref="dic">
<template #default="{ _, data }"> <template #default="{ _, data }">

View File

@ -121,7 +121,7 @@ export default {
this.apis = [] this.apis = []
const ips = data.data.rows?.map((x) => x.createdClientIp) ?? [] const ips = data.data.rows?.map((x) => x.createdClientIp) ?? []
const res = await Promise.all([ const res = await Promise.all([
ips && ips.length > 0 ? http.get(`http://ip.line92.xyz/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })), ips && ips.length > 0 ? http.get(`https://ip.tools92.top/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })),
]) ])
this.ips = res[0] this.ips = res[0]
}, },

View File

@ -239,7 +239,7 @@ export default {
}) })
: new Promise((x) => x({ data: [] })), : new Promise((x) => x({ data: [] })),
ips && ips.length > 0 ? http.get(`http://ip.line92.xyz/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })), ips && ips.length > 0 ? http.get(`https://ip.tools92.top/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })),
]) ])
this.owners = res[0].data this.owners = res[0].data
this.apis = res[1].data this.apis = res[1].data