初始化小程序端

This commit is contained in:
2025-12-11 11:06:11 +08:00
parent a2c0d17210
commit f9988ae675
531 changed files with 235994 additions and 114 deletions

View File

@@ -14,7 +14,8 @@ App({
}) })
}, },
globalData: { globalData: {
userInfo: null userInfo: null,
baseUrl: 'https://你的域名' // 后端 API 根地址
} }
}) })

View File

@@ -1,15 +1,35 @@
{ {
"pages": [ "pages": [
"pages/index/index", "pages/logs/logs",
"pages/logs/logs" "pages/chat/chat"
], ],
"window": { "window": {
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
"navigationBarTitleText": "Weixin", "navigationBarTitleText": "宝来威智能AI",
"navigationBarBackgroundColor": "#ffffff" "navigationBarBackgroundColor": "#ffffff"
}, },
"__usePrivacyCheck__": true,
"style": "v2", "style": "v2",
"componentFramework": "glass-easel", "componentFramework": "glass-easel",
"sitemapLocation": "sitemap.json", "sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents" "lazyCodeLoading": "requiredComponents",
} "plugins": {
"WechatSI": {
"version": "0.3.6",
"provider": "wx069ba97219f66d99"
}
},
"requiredPrivateInfos": ["getLocation"] ,
"permission": {
"scope.userLocation": {
"desc": "用于打卡/显示附近门店"
},
"scope.record": {
"desc": "需要录音权限以实现语音输入功能"
},
"scope.speechRecognition": { "desc": "需要语音识别权限" },
"requiredBackgroundModes": ["audio"]
}
}

View File

@@ -1,18 +1,247 @@
// logs.js import { authorizeBatch } from '../../utils/authorize'
const util = require('../../utils/util.js') const util = require('../../utils/util.js')
Page({ Page({
data: { data: {
logs: [] isAgree: false,
}, openid:"",
showAuthModal: false,
userInfo: null,
needReg: false,
openid: '',
avatarUrl: '/images/Blvlogo.png',
nickName: '',
form: {
name: '',
phone: ''
}
},
onLoad() { onLoad() {
this.handleLogin()
},
onAvatar(e) {
////////debugger
this.setData({ avatarUrl: e.detail.avatarUrl }) // 用户选的头像
},
onNick(e) {
this.setData({ nickName: e.detail.value }) // 用户输入的昵称
},
inputName(e) {this.setData({'form.name': e.detail.value})},
inputPhone(e) {this.setData({'form.phone': e.detail.value})},
async submitReg() {
try {
wx.showLoading({ title: '登录中...', mask: true });
// 获取微信code
const loginRes = await new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject
});
});
if (!loginRes.code) {
throw new Error('获取登录凭证失败');
}
const nickName = this.data.nickName;
let form =this.data.form
let openid =this.data.openid
let avatarUrl =this.data.avatarUrl
// 发送到后端
const apiRes = await new Promise((resolve, reject) => {
wx.request({
url: 'https://wx-xcx-check.blv-oa.com:4433/api/Login/Register',
method: 'POST',
data: {
UserName:form.name ,
UserKey: openid,
WeChatName:nickName,
PhoneNumber:form.phone,
AvatarUrl:avatarUrl
},
success: res =>{
if (res.data.success && res.data.data.userName && res.data.data.weChatName && res.data.data.phoneNumber) {
wx.setStorageSync('openid', res.data.data.userKey);
this.setData({openid:res.data.data.userKey});
wx.navigateTo({url: '/pages/chat/chat'});
}
else{
wx.showToast({
title: '登录失败:'+ res.data.message,
icon: 'none'
});
this.setData({needReg: true});
}
},
fail: reject
});
});
} catch (error) {
console.error('登录失败:', error);
wx.showToast({
title: `登录失败: ${error.message || '未知错误'}`,
icon: 'none'
});
} finally {
wx.hideLoading();
}
},
onAgreeChange(e) {
console.log(e)
this.setData({ isAgree: e.detail.value.length > 0 });
},
openContract() {
wx.openPrivacyContract({
success: () => console.log('已打开协议'),
fail: (e) => console.log('打开失败', e)
})
},
// 添加新方法
// 显示授权弹窗
showAuthModal() {
this.setData({ showAuthModal: true });
},
// 取消授权
onAuthCancel() {
this.setData({ showAuthModal: false });
wx.showToast({ title: '授权已取消', icon: 'none' });
},
// 获取用户信息
async onGetUserInfo(e) {
try {
if (e.detail.errMsg !== 'getUserInfo:ok') {
throw new Error('用户拒绝了授权');
}
const userInfo = e.detail.userInfo;
this.setData({ this.setData({
logs: (wx.getStorageSync('logs') || []).map(log => { userInfo,
return { showAuthModal: false
date: util.formatTime(new Date(log)), });
timeStamp: log //////////debugger
} // 保存到全局
}) getApp().globalData.userInfo = userInfo;
await this.onGetAuth()
// 继续登录流程
await this.completeLogin();
} catch (error) {
console.error('获取用户信息失败:'+error);
wx.showToast({
title: error.message || '获取用户信息失败',
icon: 'none'
});
}
},
async onGetAuth() {
const res = await authorizeBatch([
'scope.record',
'scope.userLocation'
])
if (true) {//if (res.ok) {
wx.showToast({ title: '全部授权成功' })
} else {
wx.showModal({
title: '提示',
content: `您拒绝了 ${res.denied.map(s => SCOPE_MAP[s]).join('、')},部分功能可能无法使用`,
showCancel: false
}) })
} }
},
// 完整的登录流程
async completeLogin() {
try {
wx.showLoading({ title: '登录中...', mask: true });
// 获取微信code
const loginRes = await new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject
});
});
if (!loginRes.code) {
throw new Error('获取登录凭证失败');
}
// 发送到后端
const apiRes = await new Promise((resolve, reject) => {
wx.request({
url: 'https://wx-xcx-check.blv-oa.com:4433/api/Login/Login',
method: 'POST',
data: {Code: loginRes.code},
success: res =>{
wx.hideLoading()
if (res.data.success){
this.setData({openid: res.data.data.userKey});
console.log(this.data.openid)
wx.setStorageSync('openid', res.data.data.userKey);
}
if (res.data.success && res.data.data.userName && res.data.data.weChatName && res.data.data.phoneNumber) {
wx.navigateTo({url: '/pages/chat/chat'});
}
else{
wx.showToast({
title: `登录失败: 用户未注册。请先填写完整信息再进行登录`,
icon: 'none'
});
this.setData({needReg: true});
}
},
fail: reject
});
});
} catch (error) {
console.error('登录失败:', error);
wx.showToast({
title: `登录失败: ${error.message || '未知错误'}`,
icon: 'none'
});
} finally {
wx.hideLoading();
}
},
// 修改原有登录方法
handleLogin: async function() {
if (this.data.isAgree) {
// 如果已有用户信息,直接完成登录
// if (this.data.userInfo) {
await this.completeLogin();
// } else {
// // 否则显示授权弹窗
// this.showAuthModal();
// }
}else{
// 提示为勾选用户协议
wx.showToast({
title: '请先勾选用户协议',
icon: 'none'
});
return; // 拦截后续登录逻辑
}
},
}) })

View File

@@ -1,6 +1,76 @@
<!--logs.wxml--> <!-- pages/login/login.wxml -->
<scroll-view class="scrollarea" scroll-y type="list"> <view class="login-container">
<block wx:for="{{logs}}" wx:key="timeStamp" wx:for-item="log"> <!-- 顶部 Logo / 标题 -->
<view class="log-item">{{index + 1}}. {{log.date}}</view> <view class="head">
</block> <image src="/images/Blvlogo.png" mode="aspectFit" class="logo"/>
</scroll-view> <text class="title">宝来威工作助手</text>
</view>
<!-- 微信登录按钮 -->
<view wx:if="{{!needReg}}" class="wx-login-box">
<button class="wx-btn" open-type="getUserProfile" bindtap="handleLogin">
微信一键登录
</button>
</view>
<!-- 信息补全表单 -->
<view wx:else class="reg-form">
<text class="tip">请补全信息</text>
<!-- 头像 -->
<!--index.wxml-->
<view class="avatar-box " >
<button open-type="chooseAvatar" bindchooseavatar="onAvatar" style="margin: 0; padding: 0;height: 32px;width:90%; ">
<image src="{{avatarUrl}}" style="height: 30px;width: 30px;" />
</button>
<text class="tip">点击更换头像</text>
</view>
<!-- 昵称 -->
<input type="nickname" class="input" placeholder="请输入昵称" bindblur="onNick" />
<input
class="input"
placeholder="用户名称"
value="{{form.name}}"
bindinput="inputName"
maxlength="20"
/>
<input
class="input"
placeholder="联系电话"
type="number"
value="{{form.phone}}"
bindinput="inputPhone"
maxlength="11"
/>
<button class="submit-btn" style="display: flex; flex-direction:column; justify-content: center; " bindtap="submitReg">进入聊天</button>
</view>
<!-- 底部协议 -->
<view class="footer">
<checkbox-group bindchange="onAgreeChange" class="agree-line">
<checkbox value="1" checked="{{isAgree}}" />
<text class="txt">登录须要勾选</text>
<text class="link" bindtap="openContract">《用户协议》</text>
</checkbox-group>
</view>
</view>
<!-- 在文件底部添加 -->
<!-- <view class="auth-modal" hidden="{{!showAuthModal}}">
<view class="auth-content">
<view class="auth-header">获取用户信息</view>
<view class="auth-body">
我们需要获取您的头像和昵称以提供更好的服务
</view>
<view class="auth-footer">
<button class="auth-btn" bindtap="onAuthCancel">取消</button>
<button class="auth-btn primary" open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">授权</button>
</view>
</view>
</view> -->

View File

@@ -14,3 +14,137 @@ page {
.log-item:last-child { .log-item:last-child {
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);
} }
/* pages/login/login.wxss */
.login-container{ height:100vh; display:flex; flex-direction:column; align-items:center; justify-content:center; background:#f5f5f5; }
.head{ margin-bottom:60rpx; text-align:center; }
.logo{ width:120rpx; height:120rpx; }
.title{ display:block; font-size:48rpx; color:#333; margin-top:20rpx; }
.wx-login-box{ width:80%; }
.wx-btn{width:100%;
height:88rpx;
/* 移除 line-height使用 Flex 布局 */
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
background:#07C160;
color:#fff;
border-radius:12rpx;
font-size:32rpx; }
.icon-wechat{ margin-right:10rpx; }
.reg-form{ width:80%; }
.tip{ font-size:28rpx; color:#666; margin-bottom:40rpx; }
.input{ width:100%; height:88rpx; background:#fff; border:1rpx solid #ddd; border-radius:12rpx; padding:0 20rpx; margin-bottom:30rpx; font-size:30rpx; }
.submit-btn{ width:100%; height:88rpx; background:#07C160; color:#fff; border-radius:12rpx; font-size:32rpx; }
.footer{ position:absolute; bottom:40rpx; font-size:24rpx; color:#999; }
.link{ color:#576B95; }
/* 授权弹窗样式 */
.auth-modal {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0,0,0,0.5);
z-index: 999;
transition: all 0.3s ease;
}
.auth-content {
background: #fff;
border-radius: 16rpx 16rpx 0 0;
padding: 30rpx;
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.auth-header {
font-size: 32rpx;
font-weight: bold;
text-align: center;
margin-bottom: 20rpx;
}
.auth-body {
font-size: 28rpx;
color: #666;
margin-bottom: 40rpx;
text-align: center;
}
.auth-footer {
display: flex;
justify-content: space-between;
}
.auth-btn {
flex: 1;
margin: 0 10rpx;
border-radius: 8rpx;
background: #f2f2f2;
color: #333;
}
.auth-btn.primary {
background: #07C160;
color: #fff;
}
/*index.wxss*/
.avatar-box {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 40rpx;
}
.avatar {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
border: 2rpx solid #e7e7e7;
}
.tip {
margin-top: 8rpx;
font-size: 24rpx;
color: #888;
}
/* wxss */
.agree-row {
display: flex;
align-items: center;
font-size: 28rpx;
}
.agree-row checkbox {
margin-right: 8rpx;
}
.link {
color: #1989fa;
margin-left: 4rpx;
}
.footer{
padding: 20rpx;
/* background: #fff; */
}
.agree-line{
display: flex;
align-items: center; /* 关键:垂直居中 */
font-size: 28rpx;
color: #333;
}
.agree-line checkbox{
margin-right: 8rpx; /* 文字和框间距 */
}
.link{
color: #1989fa;
margin-left: 4rpx;
}

View File

@@ -1,6 +1,6 @@
{ {
"compileType": "miniprogram", "compileType": "miniprogram",
"libVersion": "trial", "libVersion": "3.11.0",
"packOptions": { "packOptions": {
"ignore": [], "ignore": [],
"include": [] "include": []
@@ -36,6 +36,6 @@
"tabIndent": "auto", "tabIndent": "auto",
"tabSize": 2 "tabSize": 2
}, },
"appid": "wx1322e65d8a20d902", "appid": "wx42e9add0f91af98b",
"simulatorPluginLibVersion": {} "simulatorPluginLibVersion": {}
} }

View File

@@ -15,9 +15,10 @@
"useStaticServer": false, "useStaticServer": false,
"useLanDebug": false, "useLanDebug": false,
"showES6CompileOption": false, "showES6CompileOption": false,
"bigPackageSizeSupport": false, "bigPackageSizeSupport": true,
"checkInvalidKey": true, "checkInvalidKey": true,
"ignoreDevUnusedFiles": true "ignoreDevUnusedFiles": true
}, },
"libVersion": "3.11.0" "libVersion": "3.11.0",
"condition": {}
} }

35
WxCheckApi/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,35 @@
{
"version": "0.2.0",
"configurations": [
{
// 使用 IntelliSense 找出 C# 调试存在哪些属性
// 将悬停用于现有属性的说明
// 有关详细信息,请访问 https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md。
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// 如果已更改目标框架,请确保更新程序路径。
"program": "${workspaceFolder}/bin/Debug/net8.0/WxCheckApi.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
// 启用在启动 ASP.NET Core 时启动 Web 浏览器。有关详细信息: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

41
WxCheckApi/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/WxCheckApi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/WxCheckApi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/WxCheckApi.csproj"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -173,7 +173,56 @@
## 2. Check控制器接口 ## 2. Check控制器接口
### 2.1 添加会话记录接口 ### 2.1 检查地址接口
#### 接口描述
根据会话记录的GUID查询经纬度信息并转换为详细地址更新到数据库中。
#### 接口路径
`/api/Check/CheckAddress`
#### 请求参数
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| Guid | string | 是 | 会话唯一标识 |
#### 请求示例
```json
{
"Guid": "会话唯一标识"
}
```
#### 响应示例
**成功响应:**
```json
{
"success": true,
"message": "地址更新成功",
"address": "北京市海淀区中关村南大街5号"
}
```
**失败响应:**
```json
{
"success": false,
"message": "记录不存在或已被删除"
}
```
```json
{
"success": false,
"message": "更新失败",
"error": "数据库操作错误"
}
```
### 2.2 添加会话记录接口
#### 接口描述 #### 接口描述
添加新的会话记录到系统中。 添加新的会话记录到系统中。
@@ -188,8 +237,9 @@
| UserKey | string | 是 | 用户唯一标识键 | | UserKey | string | 是 | 用户唯一标识键 |
| ConversationContent | string | 是 | 会话内容 | | ConversationContent | string | 是 | 会话内容 |
| SendMethod | string | 是 | 发送方式 | | SendMethod | string | 是 | 发送方式 |
| UserLocation | string | 否 | 用户定位信息 | | UserLocation | string | 否 | 用户定位信息(经纬度格式:"纬度,经度" |
| MessageType | int | 否 | 1:公有消息2:私有消息 | | MessageType | int | 否 | 1:公有消息2:私有消息 |
| Guid | string | 否 | 会话唯一标识(不提供则系统自动生成) |
#### 请求示例 #### 请求示例
@@ -198,8 +248,8 @@
"UserKey": "user_123456", "UserKey": "user_123456",
"ConversationContent": "这是一条测试消息", "ConversationContent": "这是一条测试消息",
"SendMethod": "文本", "SendMethod": "文本",
"UserLocation": "北京市海淀区", "UserLocation": "39.9087,116.3975",
"MessageType": 0 "MessageType": 1
} }
``` ```
@@ -209,7 +259,9 @@
```json ```json
{ {
"success": true, "success": true,
"message": "收到!" "message": "收到!",
"conversationGuid": "会话唯一标识",
"receivedTime": "2023-10-31 10:05:00"
} }
``` ```
@@ -222,7 +274,7 @@
} }
``` ```
### 2.2 查询会话记录接口 ### 2.3 查询会话记录接口
#### 接口描述 #### 接口描述
根据用户唯一标识键查询该用户的所有会话记录。 根据用户唯一标识键查询该用户的所有会话记录。
@@ -255,27 +307,33 @@
"data": [ "data": [
{ {
"Id": 1, "Id": 1,
"Guid": "会话唯一标识",
"UserKey": "user_123456", "UserKey": "user_123456",
"ConversationContent": "这是一条测试消息", "ConversationContent": "这是一条测试消息",
"SendMethod": "文本", "SendMethod": "文本",
"UserLocation": "北京市海淀区", "UserLocation": "北京市海淀区",
"Latitude": "39.9087",
"Longitude": "116.3975",
"RecordTime": "2023-10-31T10:05:00", "RecordTime": "2023-10-31T10:05:00",
"RecordTimeUTCStamp": 1698732300000, "RecordTimeUTCStamp": 1698732300000,
"IsDeleted": false, "IsDeleted": false,
"CreateTime": "2023-10-31T10:05:00", "CreateTime": "2023-10-31T10:05:00",
"MessageType": 0 "MessageType": 1
}, },
{ {
"Id": 2, "Id": 2,
"Guid": "会话唯一标识",
"UserKey": "user_123456", "UserKey": "user_123456",
"ConversationContent": "这是第二条测试消息", "ConversationContent": "这是第二条测试消息",
"SendMethod": "图片", "SendMethod": "图片",
"UserLocation": "北京市朝阳区", "UserLocation": "北京市朝阳区",
"Latitude": "39.9180",
"Longitude": "116.4272",
"RecordTime": "2023-10-31T10:06:00", "RecordTime": "2023-10-31T10:06:00",
"RecordTimeUTCStamp": 1698732360000, "RecordTimeUTCStamp": 1698732360000,
"IsDeleted": false, "IsDeleted": false,
"CreateTime": "2023-10-31T10:06:00", "CreateTime": "2023-10-31T10:06:00",
"MessageType": 1 "MessageType": 2
} }
] ]
} }
@@ -290,7 +348,7 @@
} }
``` ```
### 2.3 分页查询会话记录接口 ### 2.4 分页查询会话记录接口
#### 接口描述 #### 接口描述
分页查询用户的会话记录每页默认10条按时间戳从**最新到最旧**排序,支持根据消息类型进行过滤。 分页查询用户的会话记录每页默认10条按时间戳从**最新到最旧**排序,支持根据消息类型进行过滤。
@@ -328,27 +386,33 @@
"conversations": [ "conversations": [
{ {
"Id": 1, "Id": 1,
"Guid": "会话唯一标识",
"UserKey": "user_123456", "UserKey": "user_123456",
"ConversationContent": "这是一条测试消息", "ConversationContent": "这是一条测试消息",
"SendMethod": "文本", "SendMethod": "文本",
"UserLocation": "北京市海淀区", "UserLocation": "北京市海淀区",
"Latitude": "39.9087",
"Longitude": "116.3975",
"RecordTime": "2023-10-31T10:00:00", "RecordTime": "2023-10-31T10:00:00",
"RecordTimeUTCStamp": 1698727200000, "RecordTimeUTCStamp": 1698727200000,
"IsDeleted": false, "IsDeleted": false,
"CreateTime": "2023-10-31T10:00:00", "CreateTime": "2023-10-31T10:00:00",
"MessageType": 0 "MessageType": 1
}, },
{ {
"Id": 2, "Id": 2,
"Guid": "会话唯一标识",
"UserKey": "user_123456", "UserKey": "user_123456",
"ConversationContent": "这是一条私有消息", "ConversationContent": "这是一条私有消息",
"SendMethod": "文本", "SendMethod": "文本",
"UserLocation": "北京市朝阳区", "UserLocation": "北京市朝阳区",
"Latitude": "39.9180",
"Longitude": "116.4272",
"RecordTime": "2023-10-31T09:55:00", "RecordTime": "2023-10-31T09:55:00",
"RecordTimeUTCStamp": 1698724500000, "RecordTimeUTCStamp": 1698724500000,
"IsDeleted": false, "IsDeleted": false,
"CreateTime": "2023-10-31T09:55:00", "CreateTime": "2023-10-31T09:55:00",
"MessageType": 1 "MessageType": 2
} }
], ],
"totalCount": 25, "totalCount": 25,
@@ -368,10 +432,73 @@
} }
``` ```
### 2.3 更新会话记录接口 ### 2.5 根据GUID查询会话记录接口
#### 接口描述 #### 接口描述
更新指定ID的会话记录需要验证UserKey的权限 根据会话唯一标识GUID查询会话记录详情不考虑IsDeleted状态
#### 接口路径
`/api/Check/GetConversationByGuid`
#### 请求参数
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| Guid | string | 是 | 会话唯一标识 |
#### 请求示例
```json
{
"Guid": "会话唯一标识"
}
```
#### 响应示例
**成功响应:**
```json
{
"success": true,
"message": "查询成功",
"data": {
"Id": 1,
"Guid": "会话唯一标识",
"UserKey": "user_123456",
"ConversationContent": "这是一条测试消息",
"SendMethod": "文本",
"UserLocation": "北京市海淀区",
"Latitude": "39.9087",
"Longitude": "116.3975",
"RecordTime": "2023-10-31T10:05:00",
"RecordTimeUTCStamp": 1698732300000,
"IsDeleted": false,
"CreateTime": "2023-10-31T10:05:00",
"MessageType": 1
}
}
```
**失败响应:**
```json
{
"success": false,
"message": "未找到该记录"
}
```
```json
{
"success": false,
"message": "查询失败",
"error": "数据库操作错误"
}
```
### 2.6 更新会话记录接口
#### 接口描述
更新指定GUID的会话记录需要验证UserKey的权限。每次更新时RecordTime会自动更新为当前时间。
#### 接口路径 #### 接口路径
`/api/Check/UpdateConversation` `/api/Check/UpdateConversation`
@@ -380,7 +507,7 @@
| 参数名 | 类型 | 必填 | 描述 | | 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------| |--------|------|------|------|
| Id | long | 是 | 会话记录ID | | Guid | string | 是 | 会话唯一标识 |
| UserKey | string | 是 | 用户唯一标识键(用于权限验证) | | UserKey | string | 是 | 用户唯一标识键(用于权限验证) |
| ConversationContent | string | 是 | 新的会话内容 | | ConversationContent | string | 是 | 新的会话内容 |
| SendMethod | string | 是 | 新的发送方式 | | SendMethod | string | 是 | 新的发送方式 |
@@ -391,12 +518,12 @@
```json ```json
{ {
"Id": 1, "Guid": "会话唯一标识",
"UserKey": "user_123456", "UserKey": "user_123456",
"ConversationContent": "更新后的会话内容", "ConversationContent": "更新后的会话内容",
"SendMethod": "文本", "SendMethod": "文本",
"UserLocation": "北京市西城区", "UserLocation": "北京市西城区",
"MessageType": 0 "MessageType": 1
} }
``` ```
@@ -406,7 +533,8 @@
```json ```json
{ {
"success": true, "success": true,
"message": "会话更新成功" "message": "更新成功",
"receivedTime": "2023-10-31 10:10:00"
} }
``` ```
@@ -414,7 +542,7 @@
```json ```json
{ {
"success": false, "success": false,
"message": "会话记录不存在或无权限修改" "message": "记录不存在或无权限修改"
} }
``` ```
@@ -426,7 +554,7 @@
} }
``` ```
### 2.4 删除会话记录接口 ### 2.7 删除会话记录接口
#### 接口描述 #### 接口描述
软删除会话记录将IsDeleted标记为1需要验证UserKey的权限。 软删除会话记录将IsDeleted标记为1需要验证UserKey的权限。
@@ -438,14 +566,14 @@
| 参数名 | 类型 | 必填 | 描述 | | 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------| |--------|------|------|------|
| Id | long | 是 | 会话记录ID | | Guid | string | 是 | 会话唯一标识 |
| UserKey | string | 是 | 用户唯一标识键(用于权限验证) | | UserKey | string | 是 | 用户唯一标识键(用于权限验证) |
#### 请求示例 #### 请求示例
```json ```json
{ {
"Id": 1, "Guid": "会话唯一标识",
"UserKey": "user_123456" "UserKey": "user_123456"
} }
``` ```
@@ -456,7 +584,7 @@
```json ```json
{ {
"success": true, "success": true,
"message": "会话删除成功" "message": "删除成功"
} }
``` ```
@@ -464,7 +592,7 @@
```json ```json
{ {
"success": false, "success": false,
"message": "会话记录不存在或已被删除" "message": "记录不存在或已被删除"
} }
``` ```
@@ -476,6 +604,73 @@
} }
``` ```
### 2.8 从Redis Stream读取消息接口
#### 接口描述
从Redis Stream读取新的会话消息。
#### 接口路径
`/api/Check/ReadMessageFromRedis`
#### 请求参数
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| GroupName | string | 否 | 消费组名称默认xcx_group |
| ConsumerName | string | 否 | 消费者名称默认consumer_时间戳 |
| Count | int | 否 | 读取消息数量默认1 |
#### 请求示例
```json
{
"GroupName": "xcx_group",
"ConsumerName": "consumer_1",
"Count": 5
}
```
#### 响应示例
**成功响应:**
```json
{
"success": true,
"message": "成功读取消息",
"data": [
{
"MessageId": "1635739200000-0",
"Id": "1",
"Guid": "会话唯一标识",
"UserKey": "user_123456",
"ConversationContent": "测试消息",
"SendMethod": "文本",
"UserLocation": "北京市海淀区",
"Latitude": "39.9087",
"Longitude": "116.3975",
"RecordTime": "2023-10-31T10:00:00",
"RecordTimeUTCStamp": "1698727200000",
"IsDeleted": "false",
"CreateTime": "2023-10-31T10:00:00",
"MessageType": "1",
"UserName": "张三",
"WeChatName": "张三的微信",
"PhoneNumber": "13800138000",
"AvatarUrl": "https://example.com/avatar.jpg"
}
]
}
```
**失败响应:**
```json
{
"success": false,
"message": "读取消息失败",
"error": "Redis操作错误"
}
```
## 3. 接口调用说明 ## 3. 接口调用说明
### 3.1 HTTP客户端调用示例JavaScript ### 3.1 HTTP客户端调用示例JavaScript
@@ -568,4 +763,4 @@ async function addConversation() {
- **最后更新时间**2023-11-01 - **最后更新时间**2023-11-01
- **维护人员**:系统管理员 - **维护人员**:系统管理员
- **版本号**v1.1.0 - **版本号**v1.2.0

View File

@@ -208,9 +208,9 @@ namespace WxCheckApi.Controllers
string latitude = ""; string latitude = "";
string longitude = ""; string longitude = "";
using (MySqlCommand cmd = new MySqlCommand("SELECT Latitude, Longitude FROM xcx_conversation WHERE Id = @Id AND IsDeleted = 0", _connection)) using (MySqlCommand cmd = new MySqlCommand("SELECT Latitude, Longitude FROM xcx_conversation WHERE Guid = @Guid AND IsDeleted = 0", _connection))
{ {
cmd.Parameters.AddWithValue("@Id", request.Id); cmd.Parameters.AddWithValue("@Guid", request.Guid);
using (var reader = await cmd.ExecuteReaderAsync()) using (var reader = await cmd.ExecuteReaderAsync())
{ {
@@ -230,9 +230,9 @@ namespace WxCheckApi.Controllers
var address = await ConvertCoordinatesToAddress(longitude, latitude); var address = await ConvertCoordinatesToAddress(longitude, latitude);
// 更新数据库中的UserLocation字段 // 更新数据库中的UserLocation字段
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET UserLocation = @UserLocation WHERE Id = @Id AND IsDeleted = 0", _connection)) using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET UserLocation = @UserLocation WHERE Guid = @Guid AND IsDeleted = 0", _connection))
{ {
cmd.Parameters.AddWithValue("@Id", request.Id); cmd.Parameters.AddWithValue("@Guid", request.Guid);
cmd.Parameters.AddWithValue("@UserLocation", address); cmd.Parameters.AddWithValue("@UserLocation", address);
int rowsAffected = await cmd.ExecuteNonQueryAsync(); int rowsAffected = await cmd.ExecuteNonQueryAsync();
@@ -261,6 +261,7 @@ namespace WxCheckApi.Controllers
[HttpPost] [HttpPost]
public async Task<IActionResult> AddConversation([FromBody] ConversationRequest request) public async Task<IActionResult> AddConversation([FromBody] ConversationRequest request)
{ {
DateTime nowtime = DateTime.Now;
try try
{ {
if (_connection.State != ConnectionState.Open) if (_connection.State != ConnectionState.Open)
@@ -288,8 +289,10 @@ namespace WxCheckApi.Controllers
} }
} }
// 生成GUID
string conversationGuid = string.IsNullOrEmpty(request.Guid) ? Guid.NewGuid().ToString("N") : request.Guid;
long conversationId = 0; long conversationId = 0;
using (MySqlCommand cmd = new MySqlCommand("INSERT INTO xcx_conversation (UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType) VALUES (@UserKey, @ConversationContent, @SendMethod, @UserLocation, @Latitude, @Longitude, @RecordTime, @RecordTimeUTCStamp, @IsDeleted, NOW(), @MessageType); SELECT LAST_INSERT_ID();", _connection)) using (MySqlCommand cmd = new MySqlCommand("INSERT INTO xcx_conversation (UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, Guid, SpeakingTime) VALUES (@UserKey, @ConversationContent, @SendMethod, @UserLocation, @Latitude, @Longitude, @RecordTime, @RecordTimeUTCStamp, @IsDeleted, @CreateTime, @MessageType, @Guid, @SpeakingTime); SELECT LAST_INSERT_ID();", _connection))
{ {
cmd.Parameters.AddWithValue("@UserKey", request.UserKey); cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
cmd.Parameters.AddWithValue("@MessageType", request.MessageType); cmd.Parameters.AddWithValue("@MessageType", request.MessageType);
@@ -298,9 +301,12 @@ namespace WxCheckApi.Controllers
cmd.Parameters.AddWithValue("@UserLocation", address); cmd.Parameters.AddWithValue("@UserLocation", address);
cmd.Parameters.AddWithValue("@Latitude", latitude); cmd.Parameters.AddWithValue("@Latitude", latitude);
cmd.Parameters.AddWithValue("@Longitude", longitude); cmd.Parameters.AddWithValue("@Longitude", longitude);
cmd.Parameters.AddWithValue("@RecordTime", DateTime.Now); cmd.Parameters.AddWithValue("@RecordTime", nowtime);
cmd.Parameters.AddWithValue("@CreateTime", nowtime);
cmd.Parameters.AddWithValue("@RecordTimeUTCStamp", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); cmd.Parameters.AddWithValue("@RecordTimeUTCStamp", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
cmd.Parameters.AddWithValue("@IsDeleted", 0); cmd.Parameters.AddWithValue("@IsDeleted", 0);
cmd.Parameters.AddWithValue("@Guid", conversationGuid);
cmd.Parameters.AddWithValue("@SpeakingTime", request.SpeakingTime);
object result = await cmd.ExecuteScalarAsync(); object result = await cmd.ExecuteScalarAsync();
conversationId = Convert.ToInt64(result); conversationId = Convert.ToInt64(result);
@@ -309,18 +315,17 @@ namespace WxCheckApi.Controllers
// 查询刚插入的记录,并左连接用户表 // 查询刚插入的记录,并左连接用户表
if (conversationId > 0) if (conversationId > 0)
{ {
string query = @"SELECT convs.Id, convs.UserKey, convs.ConversationContent, convs.SendMethod, string query = @"SELECT convs.Id, convs.Guid, convs.UserKey, convs.ConversationContent, convs.SendMethod,
convs.UserLocation, convs.Latitude, convs.Longitude, convs.RecordTime, convs.UserLocation, convs.Latitude, convs.Longitude, convs.RecordTime,
convs.RecordTimeUTCStamp, convs.IsDeleted, convs.CreateTime, convs.MessageType, convs.RecordTimeUTCStamp, convs.IsDeleted, convs.CreateTime, convs.MessageType, convs.SpeakingTime,
users.UserName, users.WeChatName, users.PhoneNumber, users.AvatarUrl users.UserName, users.WeChatName, users.PhoneNumber, users.AvatarUrl
FROM xcx_conversation AS convs FROM xcx_conversation AS convs
LEFT JOIN xcx_users AS users ON convs.UserKey = users.UserKey LEFT JOIN xcx_users AS users ON convs.UserKey = users.UserKey
WHERE convs.Id = @Id"; WHERE convs.Guid = @Guid";
using (MySqlCommand cmd = new MySqlCommand(query, _connection)) using (MySqlCommand cmd = new MySqlCommand(query, _connection))
{ {
cmd.Parameters.AddWithValue("@Id", conversationId); cmd.Parameters.AddWithValue("@Guid", conversationGuid);
using (var reader = await cmd.ExecuteReaderAsync()) using (var reader = await cmd.ExecuteReaderAsync())
{ {
if (await reader.ReadAsync()) if (await reader.ReadAsync())
@@ -329,21 +334,23 @@ namespace WxCheckApi.Controllers
var messageData = new Dictionary<string, string> var messageData = new Dictionary<string, string>
{ {
["Id"] = reader.GetInt64(0).ToString(), ["Id"] = reader.GetInt64(0).ToString(),
["UserKey"] = reader.GetString(1), ["Guid"] = reader.IsDBNull(1) ? "" : reader.GetString(1),
["ConversationContent"] = reader.GetString(2), ["UserKey"] = reader.GetString(2),
["SendMethod"] = reader.GetString(3), ["ConversationContent"] = reader.GetString(3),
["UserLocation"] = reader.IsDBNull(4) ? "" : reader.GetString(4), ["SendMethod"] = reader.GetString(4),
["Latitude"] = reader.IsDBNull(5) ? "" : reader.GetString(5), ["UserLocation"] = reader.IsDBNull(5) ? "" : reader.GetString(5),
["Longitude"] = reader.IsDBNull(6) ? "" : reader.GetString(6), ["Latitude"] = reader.IsDBNull(6) ? "" : reader.GetString(6),
["RecordTime"] = reader.GetDateTime(7).ToString("yyyy-MM-dd HH:mm:ss"), ["Longitude"] = reader.IsDBNull(7) ? "" : reader.GetString(7),
["RecordTimeUTCStamp"] = reader.GetInt64(8).ToString(), ["RecordTime"] = reader.GetDateTime(8).ToString("yyyy-MM-dd HH:mm:ss"),
["IsDeleted"] = reader.GetBoolean(9).ToString(), ["RecordTimeUTCStamp"] = reader.GetInt64(9).ToString(),
["CreateTime"] = reader.GetDateTime(10).ToString("yyyy-MM-dd HH:mm:ss"), ["IsDeleted"] = reader.GetBoolean(10).ToString(),
["MessageType"] = reader.GetInt32(11).ToString(), ["CreateTime"] = reader.GetDateTime(11).ToString("yyyy-MM-dd HH:mm:ss"),
["UserName"] = reader.IsDBNull(12) ? "" : reader.GetString(12), ["MessageType"] = reader.GetInt32(12).ToString(),
["WeChatName"] = reader.IsDBNull(13) ? "" : reader.GetString(13), ["SpeakingTime"] = reader.IsDBNull(13) ? "" : reader.GetInt32(13).ToString(),
["PhoneNumber"] = reader.IsDBNull(14) ? "" : reader.GetString(14), ["UserName"] = reader.IsDBNull(14) ? "" : reader.GetString(14),
["AvatarUrl"] = reader.IsDBNull(15) ? "" : reader.GetString(15) ["WeChatName"] = reader.IsDBNull(15) ? "" : reader.GetString(15),
["PhoneNumber"] = reader.IsDBNull(16) ? "" : reader.GetString(16),
["AvatarUrl"] = reader.IsDBNull(17) ? "" : reader.GetString(17)
}; };
// 发送到Redis Stream // 发送到Redis Stream
@@ -371,7 +378,7 @@ namespace WxCheckApi.Controllers
} }
} }
return Ok(new { success = true, message = "收到!", conversationId = conversationId }); return Ok(new { success = true, message = "收到!", conversationGuid, receivedTime = nowtime.ToString("yyyy-MM-dd HH:mm:ss") });
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -400,7 +407,7 @@ namespace WxCheckApi.Controllers
List<ConversationResponse> conversations = new List<ConversationResponse>(); List<ConversationResponse> conversations = new List<ConversationResponse>();
// 构建查询SQL根据MessageType参数决定是否添加过滤条件 // 构建查询SQL根据MessageType参数决定是否添加过滤条件
string query = "SELECT Id, UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType FROM xcx_conversation WHERE UserKey = @UserKey AND IsDeleted = 0"; string query = "SELECT Id, Guid, UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, SpeakingTime FROM xcx_conversation WHERE UserKey = @UserKey AND IsDeleted = 0";
if (request.MessageType == 1) if (request.MessageType == 1)
{ {
query += " AND MessageType = @MessageType"; query += " AND MessageType = @MessageType";
@@ -418,17 +425,19 @@ namespace WxCheckApi.Controllers
conversations.Add(new ConversationResponse conversations.Add(new ConversationResponse
{ {
Id = reader.GetInt64(0), Id = reader.GetInt64(0),
UserKey = reader.GetString(1), Guid = reader.IsDBNull(1) ? "" : reader.GetString(1),
ConversationContent = reader.GetString(2), UserKey = reader.GetString(2),
SendMethod = reader.GetString(3), ConversationContent = reader.GetString(3),
UserLocation = reader.IsDBNull(4) ? "" : reader.GetString(4), SendMethod = reader.GetString(4),
Latitude = reader.IsDBNull(5) ? "" : reader.GetString(5), UserLocation = reader.IsDBNull(5) ? "" : reader.GetString(5),
Longitude = reader.IsDBNull(6) ? "" : reader.GetString(6), Latitude = reader.IsDBNull(6) ? "" : reader.GetString(6),
RecordTime = reader.GetDateTime(7), Longitude = reader.IsDBNull(7) ? "" : reader.GetString(7),
RecordTimeUTCStamp = reader.GetInt64(8), RecordTime = reader.GetDateTime(8),
IsDeleted = reader.GetBoolean(9), RecordTimeUTCStamp = reader.GetInt64(9),
CreateTime = reader.GetDateTime(10), IsDeleted = reader.GetBoolean(10),
MessageType = reader.GetInt32(11) CreateTime = reader.GetDateTime(11),
MessageType = reader.GetInt32(12),
SpeakingTime = reader.IsDBNull(13) ? null : reader.GetInt32(13)
}); });
} }
} }
@@ -460,14 +469,16 @@ namespace WxCheckApi.Controllers
await _connection.OpenAsync(); await _connection.OpenAsync();
} }
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET ConversationContent = @ConversationContent, SendMethod = @SendMethod, UserLocation = @UserLocation, MessageType = @MessageType WHERE Id = @Id AND UserKey = @UserKey", _connection)) DateTime nowtime = DateTime.Now;
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET ConversationContent = @ConversationContent, SendMethod = @SendMethod, UserLocation = @UserLocation, MessageType = @MessageType, RecordTime = @RecordTime WHERE Guid = @Guid AND UserKey = @UserKey", _connection))
{ {
cmd.Parameters.AddWithValue("@Id", request.Id); cmd.Parameters.AddWithValue("@Guid", request.Guid);
cmd.Parameters.AddWithValue("@UserKey", request.UserKey); cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
cmd.Parameters.AddWithValue("@ConversationContent", request.ConversationContent); cmd.Parameters.AddWithValue("@ConversationContent", request.ConversationContent);
cmd.Parameters.AddWithValue("@SendMethod", request.SendMethod); cmd.Parameters.AddWithValue("@SendMethod", request.SendMethod);
cmd.Parameters.AddWithValue("@UserLocation", request.UserLocation ?? ""); cmd.Parameters.AddWithValue("@UserLocation", request.UserLocation ?? "");
cmd.Parameters.AddWithValue("@MessageType", request.MessageType); cmd.Parameters.AddWithValue("@MessageType", request.MessageType);
cmd.Parameters.AddWithValue("@RecordTime", nowtime);
int rowsAffected = await cmd.ExecuteNonQueryAsync(); int rowsAffected = await cmd.ExecuteNonQueryAsync();
@@ -477,7 +488,7 @@ namespace WxCheckApi.Controllers
} }
} }
return Ok(new { success = true, message = "更新成功" }); return Ok(new { success = true, message = "更新成功" , receivedTime = nowtime.ToString("yyyy-MM-dd HH:mm:ss") });
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -503,9 +514,9 @@ namespace WxCheckApi.Controllers
await _connection.OpenAsync(); await _connection.OpenAsync();
} }
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET IsDeleted = 1 WHERE Id = @Id AND UserKey = @UserKey AND IsDeleted = 0", _connection)) using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET IsDeleted = 1 WHERE Guid = @Guid AND UserKey = @UserKey AND IsDeleted = 0", _connection))
{ {
cmd.Parameters.AddWithValue("@Id", request.Id); cmd.Parameters.AddWithValue("@Guid", request.Guid);
cmd.Parameters.AddWithValue("@UserKey", request.UserKey); cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
int rowsAffected = await cmd.ExecuteNonQueryAsync(); int rowsAffected = await cmd.ExecuteNonQueryAsync();
@@ -530,6 +541,70 @@ namespace WxCheckApi.Controllers
} }
} }
} }
// 根据GUID查询会话记录不考虑IsDeleted状态
[HttpPost]
public async Task<IActionResult> GetConversationByGuid([FromBody] GetConversationByGuidRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
// 查询记录不考虑IsDeleted状态
string query = @"SELECT Id, Guid, UserKey, ConversationContent, SendMethod, UserLocation,
Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, SpeakingTime
FROM xcx_conversation
WHERE Guid = @Guid";
using (MySqlCommand cmd = new MySqlCommand(query, _connection))
{
cmd.Parameters.AddWithValue("@Guid", request.Guid);
using (var reader = await cmd.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
var conversation = new ConversationResponse
{
Id = reader.GetInt64(0),
Guid = reader.IsDBNull(1) ? "" : reader.GetString(1),
UserKey = reader.GetString(2),
ConversationContent = reader.GetString(3),
SendMethod = reader.GetString(4),
UserLocation = reader.IsDBNull(5) ? "" : reader.GetString(5),
Latitude = reader.IsDBNull(6) ? "" : reader.GetString(6),
Longitude = reader.IsDBNull(7) ? "" : reader.GetString(7),
RecordTime = reader.GetDateTime(8),
RecordTimeUTCStamp = reader.GetInt64(9),
IsDeleted = reader.GetBoolean(10),
CreateTime = reader.GetDateTime(11),
MessageType = reader.GetInt32(12),
SpeakingTime = reader.IsDBNull(13) ? null : reader.GetInt32(13)
};
return Ok(new { success = true, message = "查询成功", data = conversation });
}
else
{
return NotFound(new { success = false, message = "未找到该记录" });
}
}
}
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "查询失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 分页查询会话记录 // 分页查询会话记录
[HttpPost] [HttpPost]
@@ -551,7 +626,7 @@ namespace WxCheckApi.Controllers
List<ConversationResponse> conversations = new List<ConversationResponse>(); List<ConversationResponse> conversations = new List<ConversationResponse>();
// 构建分页查询SQL根据MessageType参数决定是否添加过滤条件 // 构建分页查询SQL根据MessageType参数决定是否添加过滤条件
string query = @"SELECT Id, UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType string query = @"SELECT Id, Guid, UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, SpeakingTime
FROM xcx_conversation FROM xcx_conversation
WHERE UserKey = @UserKey AND IsDeleted = 0"; WHERE UserKey = @UserKey AND IsDeleted = 0";
if (request.MessageType == 1) if (request.MessageType == 1)
@@ -577,17 +652,19 @@ namespace WxCheckApi.Controllers
conversations.Add(new ConversationResponse conversations.Add(new ConversationResponse
{ {
Id = reader.GetInt64(0), Id = reader.GetInt64(0),
UserKey = reader.GetString(1), Guid = reader.IsDBNull(1) ? "" : reader.GetString(1),
ConversationContent = reader.GetString(2), UserKey = reader.GetString(2),
SendMethod = reader.GetString(3), ConversationContent = reader.GetString(3),
UserLocation = reader.IsDBNull(4) ? "" : reader.GetString(4), SendMethod = reader.GetString(4),
Latitude = reader.IsDBNull(5) ? "" : reader.GetString(5), UserLocation = reader.IsDBNull(5) ? "" : reader.GetString(5),
Longitude = reader.IsDBNull(6) ? "" : reader.GetString(6), Latitude = reader.IsDBNull(6) ? "" : reader.GetString(6),
RecordTime = reader.GetDateTime(7), Longitude = reader.IsDBNull(7) ? "" : reader.GetString(7),
RecordTimeUTCStamp = reader.GetInt64(8), RecordTime = reader.GetDateTime(8),
IsDeleted = reader.GetBoolean(9), RecordTimeUTCStamp = reader.GetInt64(9),
CreateTime = reader.GetDateTime(10), IsDeleted = reader.GetBoolean(10),
MessageType = reader.GetInt32(11) CreateTime = reader.GetDateTime(11),
MessageType = reader.GetInt32(12),
SpeakingTime = reader.IsDBNull(13) ? null : reader.GetInt32(13)
}); });
} }
} }
@@ -718,6 +795,8 @@ namespace WxCheckApi.Controllers
public double Latitude { get; set; } public double Latitude { get; set; }
public double Longitude { get; set; } public double Longitude { get; set; }
public int MessageType { get; set; } = 1; // 1:公有2:私有 public int MessageType { get; set; } = 1; // 1:公有2:私有
public string? Guid { get; set; } // 会话唯一标识
public int? SpeakingTime { get; set; } // 对话时长
} }
public class UserKeyRequest public class UserKeyRequest
@@ -736,27 +815,37 @@ namespace WxCheckApi.Controllers
public class UpdateConversationRequest public class UpdateConversationRequest
{ {
public long Id { get; set; } public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
public string UserKey { get; set; } public string UserKey { get; set; }
public string ConversationContent { get; set; } public string ConversationContent { get; set; }
public string SendMethod { get; set; } public string SendMethod { get; set; }
public string UserLocation { get; set; } public string UserLocation { get; set; }
public int MessageType { get; set; } = 0; public int MessageType { get; set; } = 0;
public int? SpeakingTime { get; set; }
} }
public class DeleteConversationRequest public class DeleteConversationRequest
{ {
public long Id { get; set; } public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
public string UserKey { get; set; } public string UserKey { get; set; }
} }
public class CheckAddressRequest public class CheckAddressRequest
{ {
public long Id { get; set; } public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
}
public class GetConversationByGuidRequest
{
public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
} }
public class ConversationResponse public class ConversationResponse
{ {
public long Id { get; set; } public long Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
public string UserKey { get; set; } public string UserKey { get; set; }
public string ConversationContent { get; set; } public string ConversationContent { get; set; }
public string SendMethod { get; set; } public string SendMethod { get; set; }
@@ -768,6 +857,7 @@ namespace WxCheckApi.Controllers
public bool IsDeleted { get; set; } public bool IsDeleted { get; set; }
public DateTime CreateTime { get; set; } public DateTime CreateTime { get; set; }
public int MessageType { get; set; } // 1:公有消息2:私有消息 public int MessageType { get; set; } // 1:公有消息2:私有消息
public int? SpeakingTime { get; set; }
} }
public class RedisMessageRequest public class RedisMessageRequest

View File

@@ -11,7 +11,7 @@ using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Text.Json; using System.Text.Json;
namespace WxCheckApi.Controllers namespace WxCheckMvc.Controllers
{ {
[Route("api/[controller]/[action]")] [Route("api/[controller]/[action]")]
[ApiController] [ApiController]

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<DeleteExistingFiles>true</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net8.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ProjectGuid>e545c738-a21b-71f3-9fb9-a68d8018822d</ProjectGuid>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<_PublishTargetUrl>E:\Project_Class\WX_XCX\WxCheck_Wx_Prod\WxCheckApi\bin\Release\net8.0\publish\</_PublishTargetUrl>
<History>True|2025-12-05T03:46:42.6952752Z||;True|2025-12-03T17:28:08.6000818+08:00||;True|2025-12-03T15:36:17.3153352+08:00||;True|2025-12-03T15:34:35.0408800+08:00||;True|2025-12-03T15:32:13.7754473+08:00||;True|2025-12-03T15:23:43.3405041+08:00||;True|2025-12-03T11:08:15.0823391+08:00||;True|2025-12-03T11:04:29.8829020+08:00||;True|2025-12-03T11:00:07.4056298+08:00||;True|2025-12-03T10:56:38.5220608+08:00||;True|2025-12-03T10:51:59.6142114+08:00||;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View File

@@ -1,3 +1,4 @@
<Solution> <Solution>
<Project Path="../WxCheckMvc/WxCheckMvc.csproj" Id="5f5aee53-ea7f-4a13-a039-d664f136a7f8" />
<Project Path="WxCheckApi.csproj" /> <Project Path="WxCheckApi.csproj" />
</Solution> </Solution>

View File

@@ -11,7 +11,7 @@
Target Server Version : 120002 (12.0.2-MariaDB) Target Server Version : 120002 (12.0.2-MariaDB)
File Encoding : 65001 File Encoding : 65001
Date: 07/11/2025 15:03:55 Date: 05/12/2025 11:40:09
*/ */
SET NAMES utf8mb4; SET NAMES utf8mb4;
@@ -34,13 +34,16 @@ CREATE TABLE `xcx_conversation` (
`CreateTime` datetime NULL DEFAULT current_timestamp() COMMENT '创建时间', `CreateTime` datetime NULL DEFAULT current_timestamp() COMMENT '创建时间',
`Latitude` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '纬度', `Latitude` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '纬度',
`Longitude` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '经度', `Longitude` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '经度',
`Guid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'GUID',
`SpeakingTime` int(11) NULL DEFAULT NULL COMMENT '对话时长',
PRIMARY KEY (`Id`) USING BTREE, PRIMARY KEY (`Id`) USING BTREE,
INDEX `idx_userkey`(`UserKey` ASC) USING BTREE, INDEX `idx_userkey`(`UserKey` ASC) USING BTREE,
INDEX `idx_utcstamp`(`RecordTimeUTCStamp` ASC) USING BTREE, INDEX `idx_utcstamp`(`RecordTimeUTCStamp` ASC) USING BTREE,
INDEX `idx_deleted`(`IsDeleted` ASC) USING BTREE, INDEX `idx_deleted`(`IsDeleted` ASC) USING BTREE,
INDEX `idx_recordtime`(`RecordTime` ASC) USING BTREE, INDEX `idx_recordtime`(`RecordTime` ASC) USING BTREE,
INDEX `idx_messagetype`(`MessageType` ASC) USING BTREE INDEX `idx_messagetype`(`MessageType` ASC) USING BTREE,
) ENGINE = InnoDB AUTO_INCREMENT = 57 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会话记录表' ROW_FORMAT = Dynamic; INDEX `idx_guid`(`Guid` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 418 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会话记录表' ROW_FORMAT = Dynamic;
-- ---------------------------- -- ----------------------------
-- Table structure for xcx_log -- Table structure for xcx_log
@@ -78,6 +81,6 @@ CREATE TABLE `xcx_users` (
UNIQUE INDEX `idx_userkey`(`UserKey` ASC) USING BTREE, UNIQUE INDEX `idx_userkey`(`UserKey` ASC) USING BTREE,
INDEX `idx_disabled`(`IsDisabled` ASC) USING BTREE, INDEX `idx_disabled`(`IsDisabled` ASC) USING BTREE,
INDEX `idx_logintime`(`FirstLoginTime` ASC) USING BTREE INDEX `idx_logintime`(`FirstLoginTime` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '小程序用户表' ROW_FORMAT = Dynamic; ) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '小程序用户表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "10.0.0",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}

View File

@@ -0,0 +1,199 @@
using CSRedis;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using static CSRedis.CSRedisClient;
namespace Common
{
/// <summary>
/// Redis缓存辅助类
/// </summary>
public class CSRedisCacheHelper
{
public static CSRedisClient? redis;
public static CSRedisClient? redis14;
public static CSRedisClient? redis15;
private const string ip = "127.0.0.1";
//private const string port = "6379";
private const string port = "6800";
static CSRedisCacheHelper()
{
var redisHostStr = string.Format("{0}:{1}", ip, port);
if (!string.IsNullOrEmpty(redisHostStr))
{
redis = new CSRedisClient(redisHostStr + ",password=,defaultDatabase=0");
redis15 = new CSRedisClient(redisHostStr + ",password=,defaultDatabase=15");
var DingYueMsg = ("CellCorelDRAWUser", new Action<SubscribeMessageEventArgs>(async (args) =>
{
string body = args.Body;
}));
CSRedisCacheHelper.redis.Subscribe(DingYueMsg);
}
}
/// <summary>
/// 添加缓存
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="value"></param>
public static void Set<T>(string key, T value, int ExpireTime)
{
redis?.Set(key, value, ExpireTime * 60);
}
public static T Get<T>(string key)
{
return redis.Get<T>(key);
}
public static void Forever<T>(string key, T value)
{
redis.Set(key, value, -1);
}
public static void Del(string key)
{
redis.Del(key);
}
public static void ListPush<T>(string key, T value)
{
redis.LPush(key, value);
}
/// <summary>
/// 判断是否存在
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool Contains(string key)
{
bool result = redis.Exists(key);
return result;
}
public static string? XAdd(string key, params (string, string)[] fieldValues)
{
try
{
var result = redis.XAdd(key, fieldValues);
return result;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"XAdd error: {ex.Message}");
return null;
}
}
public static string? XReadGroup(string key, string group, string consumer, int count = 1, string id = null)
{
try
{
id = id ?? ">";
var result = redis.XReadGroup(group, consumer, count, 0, (key, id));
if (result != null && result.Length > 0)
{
// 处理消息
var messages = new List<Dictionary<string, object>>();
foreach (var streamResult in result)
{
foreach (var entry in streamResult.data)
{
var message = new Dictionary<string, object>
{
["Id"] = entry.id,
["Values"] = entry.items
};
messages.Add(message);
// 确认消息已处理
redis.XAck(key, group, entry.id);
}
}
return System.Text.Json.JsonSerializer.Serialize(messages);
}
return null;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"XReadGroup error: {ex.Message}");
return null;
}
}
public static (string key, (string id, string items)[] data)[] XReadGroup(string group, string consumer, long count, long block, params (string key, string id)[] streams)
{
try
{
var result = redis.XReadGroup(group, consumer, count, block, streams);
if (result != null && result.Length > 0)
{
// 处理消息并确认已处理
var processedResults = new List<(string key, (string id, string items)[] data)>();
foreach (var streamResult in result)
{
var messages = new List<(string id, string items)>();
foreach (var entry in streamResult.data)
{
// 确认消息已处理
redis.XAck(streamResult.key, group, entry.id);
// entry是一个元组 (string id, string[] items)
// 我们需要将string[] items转换为string items
var itemsArray = entry.items;
var itemsString = string.Join(",", itemsArray);
messages.Add((entry.id, itemsString));
}
processedResults.Add((streamResult.key, messages.ToArray()));
}
return processedResults.ToArray();
}
return Array.Empty<(string key, (string id, string items)[] data)>();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"XReadGroup error: {ex.Message}");
return Array.Empty<(string key, (string id, string items)[] data)>();
}
}
public static bool XGroupCreate(string key, string group, string id = "0")
{
try
{
redis.XGroupCreate(key, group, id, true);
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 发布消息
/// </summary>
/// <param name="Topic"></param>
/// <param name="Payload"></param>
public static void Publish(string Topic, string Payload)
{
CSRedisCacheHelper.redis.PublishNoneMessageId(Topic, Payload);
}
}
}

View File

@@ -0,0 +1,960 @@
using Common;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Hosting;
using System.IO;
namespace WxCheckMvc.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class CheckController : ControllerBase
{
private readonly MySqlConnection _connection;
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _environment;
public CheckController(MySqlConnection connection, IHttpClientFactory httpClientFactory, IConfiguration configuration, IWebHostEnvironment environment)
{
_connection = connection;
_httpClient = httpClientFactory.CreateClient();
_configuration = configuration;
_environment = environment;
}
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file, [FromForm] string rootPathType, [FromForm] string userKey)
{
try
{
if (file == null || file.Length == 0)
{
return BadRequest(new { success = false, message = "请选择文件" });
}
// 1. 确定保存路径
// 默认使用 Avatar如果传入了 rootPathType 则使用传入值
string folderName = "Avatar";
if (!string.IsNullOrWhiteSpace(rootPathType))
{
// 安全检查:只允许字母、数字、下划线,防止路径遍历
if (rootPathType.Any(c => !char.IsLetterOrDigit(c) && c != '_'))
{
return BadRequest(new { success = false, message = "rootPathType 包含非法字符,只允许字母、数字和下划线" });
}
folderName = rootPathType;
}
string webRootPath = _environment.WebRootPath;
string uploadDir = Path.Combine(webRootPath, folderName);
// 如果文件夹不存在,则创建
if (!Directory.Exists(uploadDir))
{
Directory.CreateDirectory(uploadDir);
}
// 2. 生成唯一文件名并保存
string fileExtension = Path.GetExtension(file.FileName);
string newFileName = Guid.NewGuid().ToString("N") + fileExtension;
string filePath = Path.Combine(uploadDir, newFileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
// 构建返回URL
string relativePath = $"/{folderName}/{newFileName}";
string fullUrl = $"{Request.Scheme}://{Request.Host}{relativePath}";
// 3. 如果提供了 UserKey更新数据库中的 AvatarUrl
if (!string.IsNullOrWhiteSpace(userKey))
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
string updateSql = "UPDATE xcx_users SET AvatarUrl = @AvatarUrl, UpdateTime = NOW() WHERE UserKey = @UserKey";
using (MySqlCommand cmd = new MySqlCommand(updateSql, _connection))
{
cmd.Parameters.AddWithValue("@AvatarUrl", fullUrl);
cmd.Parameters.AddWithValue("@UserKey", userKey);
await cmd.ExecuteNonQueryAsync();
}
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
return Ok(new { success = true, message = "上传成功", url = fullUrl, path = relativePath });
}
catch (Exception ex)
{
// 确保连接关闭
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
return StatusCode(500, new { success = false, message = "上传失败", error = ex.Message });
}
}
// 将经纬度转换为地址信息
public async Task<string> ConvertCoordinatesToAddress(string longitude,string latitude)
{
try
{
// 使用高德地图API进行逆地理编码
string apiKey = _configuration["AmapApi:ApiKey"] ?? "4d5cb7818664ada68ae5f68783b8bd4c";
string url = $"https://restapi.amap.com/v3/geocode/regeo?output=json&location={longitude},{latitude}&key={apiKey}&radius=1000&extensions=all";
var response = await _httpClient.GetStringAsync(url);
var jsonDoc = JsonDocument.Parse(response);
var root = jsonDoc.RootElement;
if (root.GetProperty("status").GetString() == "1" && root.TryGetProperty("regeocode", out var regeocodeElement) && regeocodeElement.ValueKind != JsonValueKind.Null)
{
if (regeocodeElement.TryGetProperty("formatted_address", out var formatted_address))
{
return formatted_address.ToString();
}
if (regeocodeElement.TryGetProperty("addressComponent", out var addressComponent))
{
string province = addressComponent.TryGetProperty("province", out var provinceElement) && provinceElement.ValueKind == JsonValueKind.String ? provinceElement.GetString() : "";
string city = addressComponent.TryGetProperty("city", out var cityElement) && cityElement.ValueKind == JsonValueKind.String ? cityElement.GetString() : "";
string district = addressComponent.TryGetProperty("district", out var districtElement) && districtElement.ValueKind == JsonValueKind.String ? districtElement.GetString() : "";
string township = addressComponent.TryGetProperty("township", out var townshipElement) && townshipElement.ValueKind == JsonValueKind.String ? townshipElement.GetString() : "";
// 获取街道和门牌号信息
string street = "";
string streetNumber = "";
double distance = 0;
// 方法1从addressComponent获取街道信息
if (addressComponent.TryGetProperty("streetNumber", out var streetNumberElement) && streetNumberElement.ValueKind == JsonValueKind.Object)
{
street = streetNumberElement.TryGetProperty("street", out var streetElement) && streetElement.ValueKind == JsonValueKind.String ? streetElement.GetString() : "";
streetNumber = streetNumberElement.TryGetProperty("number", out var numberElement) && numberElement.ValueKind == JsonValueKind.String ? numberElement.GetString() : "";
// 获取距离信息
if (streetNumberElement.TryGetProperty("distance", out var distanceElement) && distanceElement.ValueKind == JsonValueKind.String)
{
double.TryParse(distanceElement.GetString(), out distance);
}
}
// 方法2如果方法1没有获取到街道信息尝试从aoi信息中获取
if (string.IsNullOrEmpty(street) && regeocodeElement.TryGetProperty("aois", out var aoisElement) && aoisElement.ValueKind == JsonValueKind.Array)
{
var aoisArray = aoisElement.EnumerateArray();
foreach (var aoi in aoisArray)
{
if (aoi.TryGetProperty("name", out var aoiNameElement) && aoiNameElement.ValueKind == JsonValueKind.String)
{
street = aoiNameElement.GetString();
break; // 取第一个AOI作为街道信息
}
}
}
// 方法3如果前两种方法都没有获取到街道信息尝试从pois信息中获取
if (string.IsNullOrEmpty(street) && regeocodeElement.TryGetProperty("pois", out var poisElement) && poisElement.ValueKind == JsonValueKind.Array)
{
var poisArray = poisElement.EnumerateArray();
foreach (var poi in poisArray)
{
if (poi.TryGetProperty("name", out var poiNameElement) && poiNameElement.ValueKind == JsonValueKind.String)
{
street = poiNameElement.GetString();
break; // 取第一个POI作为街道信息
}
}
}
// 方法4如果以上方法都没有获取到街道信息尝试从formatted_address中解析
if (string.IsNullOrEmpty(street) && regeocodeElement.TryGetProperty("formatted_address", out var formattedAddressElement) && formattedAddressElement.ValueKind == JsonValueKind.String)
{
string formattedAddress = formattedAddressElement.GetString();
// 尝试从格式化地址中提取街道信息
// 格式化地址通常格式为:省 市 区 街道 具体地址
// 使用字符串数组作为分隔符
string[] separators = { " ", "省", "市", "区", "县", "镇", "街道", "路", "巷", "号" };
var addressParts = formattedAddress.Split(separators, StringSplitOptions.RemoveEmptyEntries);
// 查找可能包含街道信息的部分
for (int i = 0; i < addressParts.Length; i++)
{
var part = addressParts[i];
// 如果部分包含"路"、"街"、"巷"等关键词,可能是街道信息
if (part.Contains("路") || part.Contains("街") || part.Contains("巷") || part.Contains("道"))
{
street = part;
// 如果下一个部分存在且不是区县名称,可能是门牌号
if (i + 1 < addressParts.Length &&
!addressParts[i + 1].Contains("区") &&
!addressParts[i + 1].Contains("县"))
{
streetNumber = addressParts[i + 1];
}
break;
}
}
}
// 方法5如果仍然没有获取到街道信息尝试从nearestRoad信息中获取
if (string.IsNullOrEmpty(street) && regeocodeElement.TryGetProperty("streetNumber", out var nearestStreetElement) &&
nearestStreetElement.ValueKind == JsonValueKind.Object)
{
if (nearestStreetElement.TryGetProperty("street", out var nearestStreetNameElement) && nearestStreetNameElement.ValueKind == JsonValueKind.String)
{
street = nearestStreetNameElement.GetString();
}
}
// 构建详细地址字符串
string address = "";
if (!string.IsNullOrEmpty(province))
{
address += province;
}
if (!string.IsNullOrEmpty(city) && city != province)
{
address += " " + city;
}
if (!string.IsNullOrEmpty(district))
{
address += " " + district;
}
if (!string.IsNullOrEmpty(township))
{
address += " " + township;
}
if (!string.IsNullOrEmpty(street))
{
address += " " + street;
}
if (!string.IsNullOrEmpty(streetNumber))
{
address += " " + streetNumber;
}
// 如果有距离信息,添加到地址后面
if (distance > 0)
{
address += $" {distance:F1}米";
}
if (string.IsNullOrEmpty(address))
{
return "未获取到位置信息(高德返回值为空) " + latitude + "," + longitude;
}
return address.Trim();
}
}
return latitude + "," + longitude; ; // 如果API调用失败返回原始值
}
catch (Exception)
{
return latitude + "," + longitude; ; // 如果发生异常,返回原始值
}
}
[HttpPost]
public async Task<IActionResult> CheckAddress([FromBody] CheckAddressRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
// 从数据库查询经纬度信息
string latitude = "";
string longitude = "";
using (MySqlCommand cmd = new MySqlCommand("SELECT Latitude, Longitude FROM xcx_conversation WHERE Guid = @Guid AND IsDeleted = 0", _connection))
{
cmd.Parameters.AddWithValue("@Guid", request.Guid);
using (var reader = await cmd.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
latitude = reader.IsDBNull(0) ? "" : reader.GetString(0);
longitude = reader.IsDBNull(1) ? "" : reader.GetString(1);
}
else
{
return NotFound(new { success = false, message = "记录不存在或已被删除" });
}
}
}
// 转换经纬度为地址
var address = await ConvertCoordinatesToAddress(longitude, latitude);
// 更新数据库中的UserLocation字段
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET UserLocation = @UserLocation WHERE Guid = @Guid AND IsDeleted = 0", _connection))
{
cmd.Parameters.AddWithValue("@Guid", request.Guid);
cmd.Parameters.AddWithValue("@UserLocation", address);
int rowsAffected = await cmd.ExecuteNonQueryAsync();
if (rowsAffected == 0)
{
return NotFound(new { success = false, message = "记录不存在或已被删除" });
}
}
return Ok(new { success = true, message = "地址更新成功", address = address });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "更新失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 添加会话记录
[HttpPost]
public async Task<IActionResult> AddConversation([FromBody] ConversationRequest request)
{
DateTime nowtime = DateTime.Now;
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
// 解析经纬度并转换为地址
string address = "";
string latitude = "";
string longitude = "";
// 否则尝试从UserLocation字段解析
if (!string.IsNullOrEmpty(request.UserLocation))
{
string[] parts = request.UserLocation.Split(',');
if (parts.Length == 2)
{
if (double.TryParse(parts[0], out double lat) && double.TryParse(parts[1], out double lng))
{
longitude = lng.ToString();
latitude = lat.ToString();
address = "";// await ConvertCoordinatesToAddress(latitude, longitude);
}
}
}
// 生成GUID
string conversationGuid = string.IsNullOrEmpty(request.Guid) ? Guid.NewGuid().ToString("N") : request.Guid;
long conversationId = 0;
using (MySqlCommand cmd = new MySqlCommand("INSERT INTO xcx_conversation (UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, Guid, SpeakingTime) VALUES (@UserKey, @ConversationContent, @SendMethod, @UserLocation, @Latitude, @Longitude, @RecordTime, @RecordTimeUTCStamp, @IsDeleted, @CreateTime, @MessageType, @Guid, @SpeakingTime); SELECT LAST_INSERT_ID();", _connection))
{
cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
cmd.Parameters.AddWithValue("@MessageType", request.MessageType);
cmd.Parameters.AddWithValue("@ConversationContent", request.ConversationContent);
cmd.Parameters.AddWithValue("@SendMethod", request.SendMethod);
cmd.Parameters.AddWithValue("@UserLocation", address);
cmd.Parameters.AddWithValue("@Latitude", latitude);
cmd.Parameters.AddWithValue("@Longitude", longitude);
cmd.Parameters.AddWithValue("@RecordTime", nowtime);
cmd.Parameters.AddWithValue("@CreateTime", nowtime);
cmd.Parameters.AddWithValue("@RecordTimeUTCStamp", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
cmd.Parameters.AddWithValue("@IsDeleted", 0);
cmd.Parameters.AddWithValue("@Guid", conversationGuid);
cmd.Parameters.AddWithValue("@SpeakingTime", request.SpeakingTime);
object result = await cmd.ExecuteScalarAsync();
conversationId = Convert.ToInt64(result);
}
// 查询刚插入的记录,并左连接用户表
if (conversationId > 0)
{
string query = @"SELECT convs.Id, convs.Guid, convs.UserKey, convs.ConversationContent, convs.SendMethod,
convs.UserLocation, convs.Latitude, convs.Longitude, convs.RecordTime,
convs.RecordTimeUTCStamp, convs.IsDeleted, convs.CreateTime, convs.MessageType, convs.SpeakingTime,
users.UserName, users.WeChatName, users.PhoneNumber, users.AvatarUrl
FROM xcx_conversation AS convs
LEFT JOIN xcx_users AS users ON convs.UserKey = users.UserKey
WHERE convs.Guid = @Guid";
using (MySqlCommand cmd = new MySqlCommand(query, _connection))
{
cmd.Parameters.AddWithValue("@Guid", conversationGuid);
using (var reader = await cmd.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
// 构建要发送到Redis的数据
var messageData = new Dictionary<string, string>
{
["Id"] = reader.GetInt64(0).ToString(),
["Guid"] = reader.IsDBNull(1) ? "" : reader.GetString(1),
["UserKey"] = reader.GetString(2),
["ConversationContent"] = reader.GetString(3),
["SendMethod"] = reader.GetString(4),
["UserLocation"] = reader.IsDBNull(5) ? "" : reader.GetString(5),
["Latitude"] = reader.IsDBNull(6) ? "" : reader.GetString(6),
["Longitude"] = reader.IsDBNull(7) ? "" : reader.GetString(7),
["RecordTime"] = reader.GetDateTime(8).ToString("yyyy-MM-dd HH:mm:ss"),
["RecordTimeUTCStamp"] = reader.GetInt64(9).ToString(),
["IsDeleted"] = reader.GetBoolean(10).ToString(),
["CreateTime"] = reader.GetDateTime(11).ToString("yyyy-MM-dd HH:mm:ss"),
["MessageType"] = reader.GetInt32(12).ToString(),
["SpeakingTime"] = reader.IsDBNull(13) ? "" : reader.GetInt32(13).ToString(),
["UserName"] = reader.IsDBNull(14) ? "" : reader.GetString(14),
["WeChatName"] = reader.IsDBNull(15) ? "" : reader.GetString(15),
["PhoneNumber"] = reader.IsDBNull(16) ? "" : reader.GetString(16),
["AvatarUrl"] = reader.IsDBNull(17) ? "" : reader.GetString(17)
};
// 发送到Redis Stream
try
{
// 确保Stream和Group存在
CSRedisCacheHelper.XGroupCreate("xcx_msg", "xcx_group", "0");
// 将Dictionary转换为params (string, string)[]格式
var fieldValues = messageData.SelectMany(kvp => new (string, string)[] { (kvp.Key, kvp.Value) }).ToArray();
// 添加消息到Stream
string messageId = CSRedisCacheHelper.XAdd("xcx_msg", fieldValues);
// 记录日志(可选)
System.Diagnostics.Debug.WriteLine($"消息已发送到Redis Stream: {messageId}");
}
catch (Exception ex)
{
// 记录错误但不影响主流程
System.Diagnostics.Debug.WriteLine($"发送到Redis Stream失败: {ex.Message}");
}
}
}
}
}
return Ok(new { success = true, message = "收到!", conversationGuid, receivedTime = nowtime.ToString("yyyy-MM-dd HH:mm:ss") });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "发送失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 根据UserKey查询会话记录
[HttpPost]
public async Task<IActionResult> GetConversations([FromBody] UserKeyRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
List<ConversationResponse> conversations = new List<ConversationResponse>();
// 构建查询SQL根据MessageType参数决定是否添加过滤条件
string query = "SELECT Id, Guid, UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, SpeakingTime FROM xcx_conversation WHERE UserKey = @UserKey AND IsDeleted = 0";
if (request.MessageType == 1)
{
query += " AND MessageType = @MessageType";
}
query += " ORDER BY RecordTimeUTCStamp DESC";
using (MySqlCommand cmd = new MySqlCommand(query, _connection))
{
cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
conversations.Add(new ConversationResponse
{
Id = reader.GetInt64(0),
Guid = reader.IsDBNull(1) ? "" : reader.GetString(1),
UserKey = reader.GetString(2),
ConversationContent = reader.GetString(3),
SendMethod = reader.GetString(4),
UserLocation = reader.IsDBNull(5) ? "" : reader.GetString(5),
Latitude = reader.IsDBNull(6) ? "" : reader.GetString(6),
Longitude = reader.IsDBNull(7) ? "" : reader.GetString(7),
RecordTime = reader.GetDateTime(8),
RecordTimeUTCStamp = reader.GetInt64(9),
IsDeleted = reader.GetBoolean(10),
CreateTime = reader.GetDateTime(11),
MessageType = reader.GetInt32(12),
SpeakingTime = reader.IsDBNull(13) ? null : reader.GetInt32(13)
});
}
}
}
return Ok(new { success = true, data = conversations.OrderBy(z => z.RecordTimeUTCStamp) });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "查询失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 更新会话记录
[HttpPost]
public async Task<IActionResult> UpdateConversation([FromBody] UpdateConversationRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
DateTime nowtime = DateTime.Now;
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET ConversationContent = @ConversationContent, SendMethod = @SendMethod, UserLocation = @UserLocation, MessageType = @MessageType, RecordTime = @RecordTime WHERE Guid = @Guid AND UserKey = @UserKey", _connection))
{
cmd.Parameters.AddWithValue("@Guid", request.Guid);
cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
cmd.Parameters.AddWithValue("@ConversationContent", request.ConversationContent);
cmd.Parameters.AddWithValue("@SendMethod", request.SendMethod);
cmd.Parameters.AddWithValue("@UserLocation", request.UserLocation ?? "");
cmd.Parameters.AddWithValue("@MessageType", request.MessageType);
cmd.Parameters.AddWithValue("@RecordTime", nowtime);
int rowsAffected = await cmd.ExecuteNonQueryAsync();
if (rowsAffected == 0)
{
return NotFound(new { success = false, message = "记录不存在或无权限修改" });
}
}
return Ok(new { success = true, message = "更新成功" , receivedTime = nowtime.ToString("yyyy-MM-dd HH:mm:ss") });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "更新失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 软删除会话记录
[HttpPost]
public async Task<IActionResult> DeleteConversation([FromBody] DeleteConversationRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_conversation SET IsDeleted = 1 WHERE Guid = @Guid AND UserKey = @UserKey AND IsDeleted = 0", _connection))
{
cmd.Parameters.AddWithValue("@Guid", request.Guid);
cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
int rowsAffected = await cmd.ExecuteNonQueryAsync();
if (rowsAffected == 0)
{
return NotFound(new { success = false, message = "记录不存在或已被删除" });
}
}
return Ok(new { success = true, message = "删除成功" });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "删除失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 根据GUID查询会话记录不考虑IsDeleted状态
[HttpPost]
public async Task<IActionResult> GetConversationByGuid([FromBody] GetConversationByGuidRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
// 查询记录不考虑IsDeleted状态
string query = @"SELECT Id, Guid, UserKey, ConversationContent, SendMethod, UserLocation,
Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, SpeakingTime
FROM xcx_conversation
WHERE Guid = @Guid";
using (MySqlCommand cmd = new MySqlCommand(query, _connection))
{
cmd.Parameters.AddWithValue("@Guid", request.Guid);
using (var reader = await cmd.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
var conversation = new ConversationResponse
{
Id = reader.GetInt64(0),
Guid = reader.IsDBNull(1) ? "" : reader.GetString(1),
UserKey = reader.GetString(2),
ConversationContent = reader.GetString(3),
SendMethod = reader.GetString(4),
UserLocation = reader.IsDBNull(5) ? "" : reader.GetString(5),
Latitude = reader.IsDBNull(6) ? "" : reader.GetString(6),
Longitude = reader.IsDBNull(7) ? "" : reader.GetString(7),
RecordTime = reader.GetDateTime(8),
RecordTimeUTCStamp = reader.GetInt64(9),
IsDeleted = reader.GetBoolean(10),
CreateTime = reader.GetDateTime(11),
MessageType = reader.GetInt32(12),
SpeakingTime = reader.IsDBNull(13) ? null : reader.GetInt32(13)
};
return Ok(new { success = true, message = "查询成功", data = conversation });
}
else
{
return NotFound(new { success = false, message = "未找到该记录" });
}
}
}
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "查询失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 分页查询会话记录
[HttpPost]
public async Task<IActionResult> GetConversationsByPage([FromBody] PaginationRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
// 验证并设置默认值
if (request.Page < 1) request.Page = 1;
if (request.PageSize < 1 || request.PageSize > 100) request.PageSize = 10;
int offset = (request.Page - 1) * request.PageSize;
List<ConversationResponse> conversations = new List<ConversationResponse>();
// 构建分页查询SQL根据MessageType参数决定是否添加过滤条件
string query = @"SELECT Id, Guid, UserKey, ConversationContent, SendMethod, UserLocation, Latitude, Longitude, RecordTime, RecordTimeUTCStamp, IsDeleted, CreateTime, MessageType, SpeakingTime
FROM xcx_conversation
WHERE UserKey = @UserKey AND IsDeleted = 0";
if (request.MessageType == 1)
{
query += " AND MessageType = @MessageType";
}
query += " ORDER BY RecordTimeUTCStamp DESC LIMIT @Offset, @Limit";
using (MySqlCommand cmd = new MySqlCommand(query, _connection))
{
cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
if (request.MessageType == 1)
{
cmd.Parameters.AddWithValue("@MessageType", request.MessageType);
}
cmd.Parameters.AddWithValue("@Offset", offset);
cmd.Parameters.AddWithValue("@Limit", request.PageSize);
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
conversations.Add(new ConversationResponse
{
Id = reader.GetInt64(0),
Guid = reader.IsDBNull(1) ? "" : reader.GetString(1),
UserKey = reader.GetString(2),
ConversationContent = reader.GetString(3),
SendMethod = reader.GetString(4),
UserLocation = reader.IsDBNull(5) ? "" : reader.GetString(5),
Latitude = reader.IsDBNull(6) ? "" : reader.GetString(6),
Longitude = reader.IsDBNull(7) ? "" : reader.GetString(7),
RecordTime = reader.GetDateTime(8),
RecordTimeUTCStamp = reader.GetInt64(9),
IsDeleted = reader.GetBoolean(10),
CreateTime = reader.GetDateTime(11),
MessageType = reader.GetInt32(12),
SpeakingTime = reader.IsDBNull(13) ? null : reader.GetInt32(13)
});
}
}
}
// 查询总数根据MessageType参数决定是否添加过滤条件
int totalCount = 0;
string countQuery = "SELECT COUNT(*) FROM xcx_conversation WHERE UserKey = @UserKey AND IsDeleted = 0";
if (request.MessageType == 1)
{
countQuery += " AND MessageType = @MessageType";
}
using (MySqlCommand countCmd = new MySqlCommand(countQuery, _connection))
{
countCmd.Parameters.AddWithValue("@UserKey", request.UserKey);
if (request.MessageType == 1)
{
countCmd.Parameters.AddWithValue("@MessageType", request.MessageType);
}
totalCount = Convert.ToInt32(await countCmd.ExecuteScalarAsync());
}
int totalPages = (int)Math.Ceiling((double)totalCount / request.PageSize);
return Ok(new {
success = true,
data = new {
conversations = conversations.OrderBy(z => z.RecordTimeUTCStamp),
totalCount = totalCount,
page = request.Page,
pageSize = request.PageSize,
totalPages = totalPages
}
});
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "查询失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 从Redis Stream读取消息
[HttpPost]
public async Task<IActionResult> ReadMessageFromRedis([FromBody] RedisMessageRequest request)
{
try
{
// 确保Stream和Group存在
CSRedisCacheHelper.XGroupCreate("xcx_msg", "xcx_group", "0");
// 从Redis Stream读取消息
string groupName = request.GroupName ?? "xcx_group";
string consumerName = request.ConsumerName ?? "consumer_" + DateTime.Now.Ticks;
string messages = CSRedisCacheHelper.XReadGroup("xcx_msg", groupName, consumerName, request.Count ?? 1);
if (string.IsNullOrEmpty(messages))
{
return Ok(new { success = true, message = "没有新消息", data = new List<string>() });
}
// 解析消息
var messageList = new List<object>();
try
{
var messageEntries = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string, object>>>(messages);
if (messageEntries != null)
{
foreach (var entry in messageEntries)
{
if (entry.TryGetValue("Id", out var id) &&
entry.TryGetValue("Values", out var values))
{
var messageData = new Dictionary<string, string>();
// 如果Values是JsonElement需要进一步解析
if (values is JsonElement valuesElement && valuesElement.ValueKind == JsonValueKind.Object)
{
foreach (var property in valuesElement.EnumerateObject())
{
messageData[property.Name] = property.Value.GetString() ?? "";
}
}
else if (values is Dictionary<string, string> valuesDict)
{
messageData = valuesDict;
}
messageData["MessageId"] = id.ToString();
messageList.Add(messageData);
}
}
}
}
catch (Exception parseEx)
{
// 如果解析失败,尝试直接返回原始消息
System.Diagnostics.Debug.WriteLine($"解析消息失败: {parseEx.Message}");
messageList.Add(new { RawMessage = messages });
}
return Ok(new { success = true, message = "成功读取消息", data = messageList });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "读取消息失败", error = ex.Message });
}
}
}
// 请求和响应模型
public class ConversationRequest
{
public string UserKey { get; set; }
public string ConversationContent { get; set; }
public string SendMethod { get; set; }
public string UserLocation { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public int MessageType { get; set; } = 1; // 1:公有2:私有
public string? Guid { get; set; } // 会话唯一标识
public int? SpeakingTime { get; set; } // 对话时长
}
public class UserKeyRequest
{
public string UserKey { get; set; }
public int MessageType { get; set; } = 0; // 0:不判断消息类型1:公有2:私有
}
public class PaginationRequest
{
public string UserKey { get; set; }
public int Page { get; set; } = 1; // 默认第一页
public int PageSize { get; set; } = 10; // 默认每页10条
public int MessageType { get; set; } = 0; // 0:不判断消息类型1:公有2:私有
}
public class UpdateConversationRequest
{
public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
public string UserKey { get; set; }
public string ConversationContent { get; set; }
public string SendMethod { get; set; }
public string UserLocation { get; set; }
public int MessageType { get; set; } = 0;
public int? SpeakingTime { get; set; }
}
public class DeleteConversationRequest
{
public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
public string UserKey { get; set; }
}
public class CheckAddressRequest
{
public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
}
public class GetConversationByGuidRequest
{
public long? Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
}
public class ConversationResponse
{
public long Id { get; set; }
public string Guid { get; set; } // 会话唯一标识
public string UserKey { get; set; }
public string ConversationContent { get; set; }
public string SendMethod { get; set; }
public string UserLocation { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public DateTime RecordTime { get; set; }
public long RecordTimeUTCStamp { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreateTime { get; set; }
public int MessageType { get; set; } // 1:公有消息2:私有消息
public int? SpeakingTime { get; set; }
}
public class RedisMessageRequest
{
public string GroupName { get; set; } = "xcx_group";
public string ConsumerName { get; set; }
public int? Count { get; set; } = 1;
}
}

View File

@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using WxCheckMvc.Models;
namespace WxCheckMvc.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

View File

@@ -0,0 +1,324 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using MySql.Data.MySqlClient;
using System;
using System.Data;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Text.Json;
namespace WxCheckMvc.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class LoginController : ControllerBase
{
private readonly MySqlConnection _connection;
private readonly IHttpClientFactory _httpClientFactory;
public IConfiguration? configuration { get; set; }
public LoginController(MySqlConnection connection, IHttpClientFactory httpClientFactory, IConfiguration? configuration)
{
_connection = connection;
_httpClientFactory = httpClientFactory;
this.configuration = configuration;
}
// 获取微信小程序OpenID
private async Task<string> GetWxOpenIdAsync(string code)
{
try
{
var appId = configuration["WeChat:AppId"];
var appSecret = configuration["WeChat:AppSecret"];
if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(appSecret))
{
throw new Exception("微信小程序配置缺失");
}
var httpClient = _httpClientFactory.CreateClient();
var url = $"https://api.weixin.qq.com/sns/jscode2session?appid={appId}&secret={appSecret}&js_code={code}&grant_type=authorization_code";
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
var jsonDocument = JsonDocument.Parse(responseContent);
if (jsonDocument.RootElement.TryGetProperty("openid", out var openidElement))
{
return openidElement.GetString();
}
else
{
// 如果有错误信息,抛出异常
if (jsonDocument.RootElement.TryGetProperty("errcode", out var errcodeElement) &&
jsonDocument.RootElement.TryGetProperty("errmsg", out var errmsgElement))
{
throw new Exception($"获取OpenID失败: {errcodeElement.GetInt32()} - {errmsgElement.GetString()}");
}
throw new Exception("获取OpenID失败: 响应中未包含openid");
}
}
catch (Exception ex)
{
throw new Exception($"获取微信OpenID时发生错误: {ex.Message}");
}
}
private string GetToken(string entity)
{
string TokenString;
var claims = new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.Name, entity)
};
var secretByte = Encoding.UTF8.GetBytes(configuration["JwT:SecretKey"]);
var signingKey = new SymmetricSecurityKey(secretByte);
var a = SecurityAlgorithms.HmacSha256;
var signingCredentials = new SigningCredentials(signingKey, a);
var token = new JwtSecurityToken(
issuer: configuration["JwT:Issuer"],
audience: configuration["JwT:Audience"],//接收
claims: claims,//存放的用户信息
notBefore: DateTime.UtcNow,//发布时间
expires: DateTime.UtcNow.AddMonths(12),
signingCredentials: signingCredentials
//有效期设置为1天signingCredentials //数字名
);
TokenString = new JwtSecurityTokenHandler().WriteToken(token);
return TokenString;
}
// 用户注册接口
[HttpPost]
public async Task<IActionResult> Register([FromBody] RegisterRequest request)
{
try
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
// 检查用户是否存在
UserResponse user = null;
using (MySqlCommand checkCmd = new MySqlCommand("SELECT Id, UserName, UserKey, WeChatName, PhoneNumber, AvatarUrl, FirstLoginTime, IsDisabled, CreateTime, UpdateTime FROM xcx_users WHERE UserKey = @UserKey", _connection))
{
checkCmd.Parameters.AddWithValue("@UserKey", request.UserKey);
using (var reader = await checkCmd.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
user = new UserResponse
{
Id = reader.GetInt64(0),
UserName = reader.IsDBNull(1) ? "" : reader.GetString(1),
UserKey = reader.GetString(2),
WeChatName = reader.IsDBNull(3) ? "" : reader.GetString(3),
PhoneNumber = reader.IsDBNull(4) ? "" : reader.GetString(4),
AvatarUrl = reader.IsDBNull(5) ? "" : reader.GetString(5),
FirstLoginTime = reader.GetDateTime(6),
IsDisabled = reader.GetBoolean(7),
CreateTime = reader.GetDateTime(8),
UpdateTime = reader.GetDateTime(9)
};
}
}
}
if (user == null)
{
return NotFound(new { success = false, message = "用户不存在" });
}
// 更新用户信息
using (MySqlCommand cmd = new MySqlCommand("UPDATE xcx_users SET UserName = @UserName, WeChatName = @WeChatName, PhoneNumber = @PhoneNumber, AvatarUrl = @AvatarUrl, UpdateTime = NOW() WHERE UserKey = @UserKey", _connection))
{
cmd.Parameters.AddWithValue("@UserName", request.UserName ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@WeChatName", request.WeChatName ?? "");
cmd.Parameters.AddWithValue("@PhoneNumber", request.PhoneNumber ?? "");
cmd.Parameters.AddWithValue("@AvatarUrl", request.AvatarUrl ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
await cmd.ExecuteNonQueryAsync();
}
// 获取更新后的用户信息
UserResponse updatedUser = null;
using (MySqlCommand cmd = new MySqlCommand("SELECT Id, UserName, UserKey, WeChatName, PhoneNumber, AvatarUrl, FirstLoginTime, IsDisabled, CreateTime, UpdateTime FROM xcx_users WHERE UserKey = @UserKey", _connection))
{
cmd.Parameters.AddWithValue("@UserKey", request.UserKey);
using (var reader = await cmd.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
updatedUser = new UserResponse
{
Id = reader.GetInt64(0),
UserName = reader.IsDBNull(1) ? "" : reader.GetString(1),
UserKey = reader.GetString(2),
WeChatName = reader.IsDBNull(3) ? "" : reader.GetString(3),
PhoneNumber = reader.IsDBNull(4) ? "" : reader.GetString(4),
AvatarUrl = reader.IsDBNull(5) ? "" : reader.GetString(5),
FirstLoginTime = reader.GetDateTime(6),
IsDisabled = reader.GetBoolean(7),
CreateTime = reader.GetDateTime(8),
UpdateTime = reader.GetDateTime(9),
Token = GetToken(request.UserKey)
};
}
}
}
return Ok(new { success = true, data = updatedUser });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "更新用户信息失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
// 用户登录接口
[HttpPost]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
try
{
string openId;
try
{
openId = await GetWxOpenIdAsync(request.Code);
}
catch (Exception ex)
{
return BadRequest(new { success = false, message = "获取微信OpenID失败", error = ex.Message });
}
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync();
}
UserResponse user = null;
// 检查用户是否存在
using (MySqlCommand checkCmd = new MySqlCommand("SELECT COUNT(1) FROM xcx_users WHERE UserKey = @UserKey", _connection))
{
checkCmd.Parameters.AddWithValue("@UserKey", openId);
int count = Convert.ToInt32(await checkCmd.ExecuteScalarAsync());
// 如果用户不存在,则注册新用户
if (count == 0)
{
using (MySqlCommand insertCmd = new MySqlCommand("INSERT INTO xcx_users (UserKey, FirstLoginTime, IsDisabled, CreateTime, UpdateTime) VALUES (@UserKey, @FirstLoginTime, @IsDisabled, NOW(), NOW())", _connection))
{
insertCmd.Parameters.AddWithValue("@UserKey", openId);
insertCmd.Parameters.AddWithValue("@FirstLoginTime", DateTime.Now);
insertCmd.Parameters.AddWithValue("@IsDisabled", 0); // 默认启用
await insertCmd.ExecuteNonQueryAsync();
}
}
}
// 获取用户信息
using (MySqlCommand cmd = new MySqlCommand("SELECT Id, UserName, UserKey, WeChatName, PhoneNumber, AvatarUrl, FirstLoginTime, IsDisabled, CreateTime, UpdateTime FROM xcx_users WHERE UserKey = @UserKey", _connection))
{
cmd.Parameters.AddWithValue("@UserKey", openId);
using (var reader = await cmd.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
user = new UserResponse
{
Id = reader.GetInt64(0),
UserName = reader.IsDBNull(1) ? "" : reader.GetString(1),
UserKey = reader.GetString(2),
WeChatName = reader.IsDBNull(3) ? "" : reader.GetString(3),
PhoneNumber = reader.IsDBNull(4) ? "" : reader.GetString(4),
AvatarUrl = reader.IsDBNull(5) ? "" : reader.GetString(5),
FirstLoginTime = reader.GetDateTime(6),
IsDisabled = reader.GetBoolean(7),
CreateTime = reader.GetDateTime(8),
UpdateTime = reader.GetDateTime(9),
Token = GetToken(openId)
};
}
}
}
if (user == null)
{
return NotFound(new { success = false, message = "用户不存在" });
}
if (user.IsDisabled)
{
return Ok(new { success = false, message = "用户已被禁用" });
}
return Ok(new { success = true, data = user });
}
catch (Exception ex)
{
return StatusCode(500, new { success = false, message = "登录失败", error = ex.Message });
}
finally
{
if (_connection.State == ConnectionState.Open)
{
await _connection.CloseAsync();
}
}
}
}
// 请求和响应模型
public class RegisterRequest
{
public string UserName { get; set; }
public string UserKey { get; set; } // 改为直接传入UserKey
public string WeChatName { get; set; }
public string PhoneNumber { get; set; }
public string AvatarUrl { get; set; }
}
public class LoginRequest
{
public string Code { get; set; }
}
public class UserResponse
{
public long Id { get; set; }
public string UserName { get; set; }
public string UserKey { get; set; }
public string WeChatName { get; set; }
public string PhoneNumber { get; set; }
public string AvatarUrl { get; set; }
public DateTime FirstLoginTime { get; set; }
public bool IsDisabled { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public string Token { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
namespace WxCheckMvc.Models
{
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

80
WxCheckMvc/Program.cs Normal file
View File

@@ -0,0 +1,80 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using MySql.Data.MySqlClient;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// <20><><EFBFBD><EFBFBD>HttpClientFactory
builder.Services.AddHttpClient();
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݿ<EFBFBD><DDBF><EFBFBD><EFBFBD><EFBFBD>
builder.Services.AddScoped<MySqlConnection>(sp => {
var connectionString = builder.Configuration.GetConnectionString("MySQLConnection");
return new MySqlConnection(connectionString);
});
builder.Services.AddAuthorization();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(option =>
{
string DefaultKey = "B,EZipeApY3cNj3~4RP0UMR=H>9x8.1!E85wmZ]]py2d$Y?5";
var sec = Encoding.UTF8.GetBytes(builder.Configuration["JWT:SecretKey"] ?? DefaultKey);
option.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JwT:Issuer"],
ValidAudience = builder.Configuration["JwT:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(sec)
};
option.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var token = context.Request.Headers["token"].FirstOrDefault();
if (string.IsNullOrEmpty(token))
{
// <20><><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>ҵ<EFBFBD> token ͷ<><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Authorization ͷ<><CDB7>
token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
}
// <20><><EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><D2B5><EFBFBD> token<65><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>õ<EFBFBD> HttpContext <20><>
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
return Task.CompletedTask;
}
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<DeleteExistingFiles>true</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net8.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ProjectGuid>5f5aee53-ea7f-4a13-a039-d664f136a7f8</ProjectGuid>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<_PublishTargetUrl>E:\Project_Class\WX_XCX\WxCheck_Wx_Prod\WxCheckMvc\bin\Release\net8.0\publish\</_PublishTargetUrl>
<History>True|2025-12-05T10:56:51.7439135Z||;True|2025-12-05T17:44:11.4130698+08:00||;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:13325",
"sslPort": 44342
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5141",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7233;http://localhost:5141",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

View File

@@ -0,0 +1,6 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>

View File

@@ -0,0 +1,25 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

View File

@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - WxCheckMvc</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/WxCheckMvc.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">WxCheckMvc</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2025 - WxCheckMvc - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@@ -0,0 +1,48 @@
/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
a {
color: #0077cc;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}

View File

@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

View File

@@ -0,0 +1,3 @@
@using WxCheckMvc
@using WxCheckMvc.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CSRedisCore" Version="3.8.807" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.22" />
<PackageReference Include="MySql.Data" Version="8.4.0" />
<PackageReference Include="System.Text.Json" Version="8.0.6" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
<NameOfLastUsedPublishProfile>E:\Project_Class\WX_XCX\WxCheck_Wx_Prod\WxCheckMvc\Properties\PublishProfiles\FolderProfile.pubxml</NameOfLastUsedPublishProfile>
</PropertyGroup>
</Project>

24
WxCheckMvc/WxCheckMvc.sln Normal file
View File

@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WxCheckMvc", "WxCheckMvc.csproj", "{DB5FCFC7-0359-F923-6D06-09CD18BA5F14}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DB5FCFC7-0359-F923-6D06-09CD18BA5F14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DB5FCFC7-0359-F923-6D06-09CD18BA5F14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB5FCFC7-0359-F923-6D06-09CD18BA5F14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB5FCFC7-0359-F923-6D06-09CD18BA5F14}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B93F140C-6356-4EF1-9849-4C37778126C2}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,25 @@
{
"JwT": {
"SecretKey": "1%猜U36eraIYI?3s9dI}46an不Nn>P]3)$9:dCnS5=ajAu%8B5]15hF到20T20QBD]Mt9}2z76jO#Glg&0yDy7k-2zVdt&Z5ur>=l)QF2^1&Dq04m76U2P9wvlWf",
"Issuer": "微信小程序token",
"Audience": "W*u93xxp*08DnW@%6}5Tjh6bE?;hW"
},
"WeChat": {
"AppId": "wx42e9add0f91af98b",
"AppSecret": "5620f00b40297efaf3d197d61ae184d6"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MySQLConnection": "Server=47.119.147.104;Database=wx_xcx_check;user id=root;password=hbfjW6A_eob;port=3307;"
},
"AmapApi": {
"ApiKey": "4d5cb7818664ada68ae5f68783b8bd4c"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,489 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"WxCheckMvc/1.0.0": {
"dependencies": {
"CSRedisCore": "3.8.807",
"Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.22",
"MySql.Data": "8.4.0"
},
"runtime": {
"WxCheckMvc.dll": {}
}
},
"BouncyCastle.Cryptography/2.2.1": {
"runtime": {
"lib/net6.0/BouncyCastle.Cryptography.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.2.1.47552"
}
}
},
"CSRedisCore/3.8.807": {
"dependencies": {
"Newtonsoft.Json": "13.0.1"
},
"runtime": {
"lib/netstandard2.0/CSRedisCore.dll": {
"assemblyVersion": "3.8.807.0",
"fileVersion": "3.8.807.0"
}
}
},
"Google.Protobuf/3.25.1": {
"runtime": {
"lib/net5.0/Google.Protobuf.dll": {
"assemblyVersion": "3.25.1.0",
"fileVersion": "3.25.1.0"
}
}
},
"K4os.Compression.LZ4/1.3.5": {
"runtime": {
"lib/net6.0/K4os.Compression.LZ4.dll": {
"assemblyVersion": "1.3.5.0",
"fileVersion": "1.3.5.0"
}
}
},
"K4os.Compression.LZ4.Streams/1.3.5": {
"dependencies": {
"K4os.Compression.LZ4": "1.3.5",
"K4os.Hash.xxHash": "1.0.8"
},
"runtime": {
"lib/net6.0/K4os.Compression.LZ4.Streams.dll": {
"assemblyVersion": "1.3.5.0",
"fileVersion": "1.3.5.0"
}
}
},
"K4os.Hash.xxHash/1.0.8": {
"runtime": {
"lib/net6.0/K4os.Hash.xxHash.dll": {
"assemblyVersion": "1.0.8.0",
"fileVersion": "1.0.8.0"
}
}
},
"Microsoft.AspNetCore.Authentication.JwtBearer/8.0.22": {
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.1.2"
},
"runtime": {
"lib/net8.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll": {
"assemblyVersion": "8.0.22.0",
"fileVersion": "8.0.2225.52808"
}
}
},
"Microsoft.IdentityModel.Abstractions/7.1.2": {
"runtime": {
"lib/net8.0/Microsoft.IdentityModel.Abstractions.dll": {
"assemblyVersion": "7.1.2.0",
"fileVersion": "7.1.2.41121"
}
}
},
"Microsoft.IdentityModel.JsonWebTokens/7.1.2": {
"dependencies": {
"Microsoft.IdentityModel.Tokens": "7.1.2"
},
"runtime": {
"lib/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
"assemblyVersion": "7.1.2.0",
"fileVersion": "7.1.2.41121"
}
}
},
"Microsoft.IdentityModel.Logging/7.1.2": {
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "7.1.2"
},
"runtime": {
"lib/net8.0/Microsoft.IdentityModel.Logging.dll": {
"assemblyVersion": "7.1.2.0",
"fileVersion": "7.1.2.41121"
}
}
},
"Microsoft.IdentityModel.Protocols/7.1.2": {
"dependencies": {
"Microsoft.IdentityModel.Logging": "7.1.2",
"Microsoft.IdentityModel.Tokens": "7.1.2"
},
"runtime": {
"lib/net8.0/Microsoft.IdentityModel.Protocols.dll": {
"assemblyVersion": "7.1.2.0",
"fileVersion": "7.1.2.41121"
}
}
},
"Microsoft.IdentityModel.Protocols.OpenIdConnect/7.1.2": {
"dependencies": {
"Microsoft.IdentityModel.Protocols": "7.1.2",
"System.IdentityModel.Tokens.Jwt": "7.1.2"
},
"runtime": {
"lib/net8.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {
"assemblyVersion": "7.1.2.0",
"fileVersion": "7.1.2.41121"
}
}
},
"Microsoft.IdentityModel.Tokens/7.1.2": {
"dependencies": {
"Microsoft.IdentityModel.Logging": "7.1.2"
},
"runtime": {
"lib/net8.0/Microsoft.IdentityModel.Tokens.dll": {
"assemblyVersion": "7.1.2.0",
"fileVersion": "7.1.2.41121"
}
}
},
"Microsoft.Win32.SystemEvents/4.7.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Win32.SystemEvents.dll": {
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.700.19.56404"
}
},
"runtimeTargets": {
"runtimes/win/lib/netcoreapp3.0/Microsoft.Win32.SystemEvents.dll": {
"rid": "win",
"assetType": "runtime",
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.700.19.56404"
}
}
},
"MySql.Data/8.4.0": {
"dependencies": {
"BouncyCastle.Cryptography": "2.2.1",
"Google.Protobuf": "3.25.1",
"K4os.Compression.LZ4.Streams": "1.3.5",
"System.Configuration.ConfigurationManager": "4.4.1",
"System.Security.Permissions": "4.7.0",
"ZstdSharp.Port": "0.7.1"
},
"runtime": {
"lib/net8.0/MySql.Data.dll": {
"assemblyVersion": "8.4.0.0",
"fileVersion": "8.4.0.0"
}
},
"runtimeTargets": {
"runtimes/win-x64/native/comerr64.dll": {
"rid": "win-x64",
"assetType": "native",
"fileVersion": "4.1.0.0"
},
"runtimes/win-x64/native/gssapi64.dll": {
"rid": "win-x64",
"assetType": "native",
"fileVersion": "4.1.0.0"
},
"runtimes/win-x64/native/k5sprt64.dll": {
"rid": "win-x64",
"assetType": "native",
"fileVersion": "4.1.0.0"
},
"runtimes/win-x64/native/krb5_64.dll": {
"rid": "win-x64",
"assetType": "native",
"fileVersion": "4.1.0.0"
},
"runtimes/win-x64/native/krbcc64.dll": {
"rid": "win-x64",
"assetType": "native",
"fileVersion": "4.1.0.0"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Configuration.ConfigurationManager/4.4.1": {
"dependencies": {
"System.Security.Cryptography.ProtectedData": "4.4.0"
},
"runtime": {
"lib/netstandard2.0/System.Configuration.ConfigurationManager.dll": {
"assemblyVersion": "4.0.0.0",
"fileVersion": "4.6.25921.2"
}
}
},
"System.Drawing.Common/4.7.0": {
"dependencies": {
"Microsoft.Win32.SystemEvents": "4.7.0"
},
"runtime": {
"lib/netstandard2.0/System.Drawing.Common.dll": {
"assemblyVersion": "4.0.0.1",
"fileVersion": "4.6.26919.2"
}
},
"runtimeTargets": {
"runtimes/unix/lib/netcoreapp3.0/System.Drawing.Common.dll": {
"rid": "unix",
"assetType": "runtime",
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.700.19.56404"
},
"runtimes/win/lib/netcoreapp3.0/System.Drawing.Common.dll": {
"rid": "win",
"assetType": "runtime",
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.700.19.56404"
}
}
},
"System.IdentityModel.Tokens.Jwt/7.1.2": {
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "7.1.2",
"Microsoft.IdentityModel.Tokens": "7.1.2"
},
"runtime": {
"lib/net8.0/System.IdentityModel.Tokens.Jwt.dll": {
"assemblyVersion": "7.1.2.0",
"fileVersion": "7.1.2.41121"
}
}
},
"System.Security.Cryptography.ProtectedData/4.4.0": {
"runtime": {
"lib/netstandard2.0/System.Security.Cryptography.ProtectedData.dll": {
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.6.25519.3"
}
},
"runtimeTargets": {
"runtimes/win/lib/netstandard2.0/System.Security.Cryptography.ProtectedData.dll": {
"rid": "win",
"assetType": "runtime",
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.6.25519.3"
}
}
},
"System.Security.Permissions/4.7.0": {
"dependencies": {
"System.Windows.Extensions": "4.7.0"
},
"runtime": {
"lib/netcoreapp3.0/System.Security.Permissions.dll": {
"assemblyVersion": "4.0.3.0",
"fileVersion": "4.700.19.56404"
}
}
},
"System.Windows.Extensions/4.7.0": {
"dependencies": {
"System.Drawing.Common": "4.7.0"
},
"runtime": {
"lib/netcoreapp3.0/System.Windows.Extensions.dll": {
"assemblyVersion": "4.0.1.0",
"fileVersion": "4.700.19.56404"
}
},
"runtimeTargets": {
"runtimes/win/lib/netcoreapp3.0/System.Windows.Extensions.dll": {
"rid": "win",
"assetType": "runtime",
"assemblyVersion": "4.0.1.0",
"fileVersion": "4.700.19.56404"
}
}
},
"ZstdSharp.Port/0.7.1": {
"runtime": {
"lib/net7.0/ZstdSharp.dll": {
"assemblyVersion": "0.7.1.0",
"fileVersion": "0.7.1.0"
}
}
}
}
},
"libraries": {
"WxCheckMvc/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"BouncyCastle.Cryptography/2.2.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==",
"path": "bouncycastle.cryptography/2.2.1",
"hashPath": "bouncycastle.cryptography.2.2.1.nupkg.sha512"
},
"CSRedisCore/3.8.807": {
"type": "package",
"serviceable": true,
"sha512": "sha512-fu0ZGIRdq1q0dZR+ecJxajfdLiRNWBR+UKx9Ob43rSwPQT/9duIJ8DLThkDjlx/0CHtX8oktv3rYAHS/mIy8bw==",
"path": "csrediscore/3.8.807",
"hashPath": "csrediscore.3.8.807.nupkg.sha512"
},
"Google.Protobuf/3.25.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Sw9bq4hOD+AaS3RrnmP5IT25cyZ/T1qpM0e8+G+23Nojhv7+ScJFPEAQo1m4EFQWhXoI4FRZDrK+wjHCPw9yxg==",
"path": "google.protobuf/3.25.1",
"hashPath": "google.protobuf.3.25.1.nupkg.sha512"
},
"K4os.Compression.LZ4/1.3.5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-TS4mqlT0X1OlnvOGNfl02QdVUhuqgWuCnn7UxupIa7C9Pb6qlQ5yZA2sPhRh0OSmVULaQU64KV4wJuu//UyVQQ==",
"path": "k4os.compression.lz4/1.3.5",
"hashPath": "k4os.compression.lz4.1.3.5.nupkg.sha512"
},
"K4os.Compression.LZ4.Streams/1.3.5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-M0NufZI8ym3mm6F6HMSPz1jw7TJGdY74fjAtbIXATmnAva/8xLz50eQZJI9tf9mMeHUaFDg76N1BmEh8GR5zeA==",
"path": "k4os.compression.lz4.streams/1.3.5",
"hashPath": "k4os.compression.lz4.streams.1.3.5.nupkg.sha512"
},
"K4os.Hash.xxHash/1.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Wp2F7BamQ2Q/7Hk834nV9vRQapgcr8kgv9Jvfm8J3D0IhDqZMMl+a2yxUq5ltJitvXvQfB8W6K4F4fCbw/P6YQ==",
"path": "k4os.hash.xxhash/1.0.8",
"hashPath": "k4os.hash.xxhash.1.0.8.nupkg.sha512"
},
"Microsoft.AspNetCore.Authentication.JwtBearer/8.0.22": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3lqhBK+t4u8Ajl2je5UC9jCoDI+8zLz/YBVjwxQKfFF9NyzACf4QQmlmKnpH/LdkVSxCjLwvJ1ko4k0EAgy8cg==",
"path": "microsoft.aspnetcore.authentication.jwtbearer/8.0.22",
"hashPath": "microsoft.aspnetcore.authentication.jwtbearer.8.0.22.nupkg.sha512"
},
"Microsoft.IdentityModel.Abstractions/7.1.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-33eTIA2uO/L9utJjZWbKsMSVsQf7F8vtd6q5mQX7ZJzNvCpci5fleD6AeANGlbbb7WX7XKxq9+Dkb5e3GNDrmQ==",
"path": "microsoft.identitymodel.abstractions/7.1.2",
"hashPath": "microsoft.identitymodel.abstractions.7.1.2.nupkg.sha512"
},
"Microsoft.IdentityModel.JsonWebTokens/7.1.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cloLGeZolXbCJhJBc5OC05uhrdhdPL6MWHuVUnkkUvPDeK7HkwThBaLZ1XjBQVk9YhxXE2OvHXnKi0PLleXxDg==",
"path": "microsoft.identitymodel.jsonwebtokens/7.1.2",
"hashPath": "microsoft.identitymodel.jsonwebtokens.7.1.2.nupkg.sha512"
},
"Microsoft.IdentityModel.Logging/7.1.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-YCxBt2EeJP8fcXk9desChkWI+0vFqFLvBwrz5hBMsoh0KJE6BC66DnzkdzkJNqMltLromc52dkdT206jJ38cTw==",
"path": "microsoft.identitymodel.logging/7.1.2",
"hashPath": "microsoft.identitymodel.logging.7.1.2.nupkg.sha512"
},
"Microsoft.IdentityModel.Protocols/7.1.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-SydLwMRFx6EHPWJ+N6+MVaoArN1Htt92b935O3RUWPY1yUF63zEjvd3lBu79eWdZUwedP8TN2I5V9T3nackvIQ==",
"path": "microsoft.identitymodel.protocols/7.1.2",
"hashPath": "microsoft.identitymodel.protocols.7.1.2.nupkg.sha512"
},
"Microsoft.IdentityModel.Protocols.OpenIdConnect/7.1.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-6lHQoLXhnMQ42mGrfDkzbIOR3rzKM1W1tgTeMPLgLCqwwGw0d96xFi/UiX/fYsu7d6cD5MJiL3+4HuI8VU+sVQ==",
"path": "microsoft.identitymodel.protocols.openidconnect/7.1.2",
"hashPath": "microsoft.identitymodel.protocols.openidconnect.7.1.2.nupkg.sha512"
},
"Microsoft.IdentityModel.Tokens/7.1.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-oICJMqr3aNEDZOwnH5SK49bR6Z4aX0zEAnOLuhloumOSuqnNq+GWBdQyrgILnlcT5xj09xKCP/7Y7gJYB+ls/g==",
"path": "microsoft.identitymodel.tokens/7.1.2",
"hashPath": "microsoft.identitymodel.tokens.7.1.2.nupkg.sha512"
},
"Microsoft.Win32.SystemEvents/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==",
"path": "microsoft.win32.systemevents/4.7.0",
"hashPath": "microsoft.win32.systemevents.4.7.0.nupkg.sha512"
},
"MySql.Data/8.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-NA273x7ybutfGwGbF2cd8rLVM5t7AkZCzRHr/+tGms1FeMlfl+LgfjHXcb5qN1QxFpeNQQKZ+vqZw8v/S8gUiA==",
"path": "mysql.data/8.4.0",
"hashPath": "mysql.data.8.4.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Configuration.ConfigurationManager/4.4.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-jz3TWKMAeuDEyrPCK5Jyt4bzQcmzUIMcY9Ud6PkElFxTfnsihV+9N/UCqvxe1z5gc7jMYAnj7V1COMS9QKIuHQ==",
"path": "system.configuration.configurationmanager/4.4.1",
"hashPath": "system.configuration.configurationmanager.4.4.1.nupkg.sha512"
},
"System.Drawing.Common/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-v+XbyYHaZjDfn0ENmJEV1VYLgGgCTx1gnfOBcppowbpOAriglYgGCvFCPr2EEZyBvXlpxbEsTwkOlInl107ahA==",
"path": "system.drawing.common/4.7.0",
"hashPath": "system.drawing.common.4.7.0.nupkg.sha512"
},
"System.IdentityModel.Tokens.Jwt/7.1.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Thhbe1peAmtSBFaV/ohtykXiZSOkx59Da44hvtWfIMFofDA3M3LaVyjstACf2rKGn4dEDR2cUpRAZ0Xs/zB+7Q==",
"path": "system.identitymodel.tokens.jwt/7.1.2",
"hashPath": "system.identitymodel.tokens.jwt.7.1.2.nupkg.sha512"
},
"System.Security.Cryptography.ProtectedData/4.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog==",
"path": "system.security.cryptography.protecteddata/4.4.0",
"hashPath": "system.security.cryptography.protecteddata.4.4.0.nupkg.sha512"
},
"System.Security.Permissions/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==",
"path": "system.security.permissions/4.7.0",
"hashPath": "system.security.permissions.4.7.0.nupkg.sha512"
},
"System.Windows.Extensions/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==",
"path": "system.windows.extensions/4.7.0",
"hashPath": "system.windows.extensions.4.7.0.nupkg.sha512"
},
"ZstdSharp.Port/0.7.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Idgg+mJEyAujqDPzA3APy9dNoyw0YQcNA65GgYjktDRtJ+nvx/hv+J+m6Eax3JJMGEYGy04oc5YNP6ZvQ3Y1vQ==",
"path": "zstdsharp.port/0.7.1",
"hashPath": "zstdsharp.port.0.7.1.nupkg.sha512"
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,19 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "8.0.0"
}
],
"configProperties": {
"System.GC.Server": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More