231
This commit is contained in:
parent
368d71d977
commit
d8b86a4f3a
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -279,3 +279,4 @@ dist
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
server/C#/HoneyBox/src/HoneyBox.Api/bin/*
|
server/C#/HoneyBox/src/HoneyBox.Api/bin/*
|
||||||
|
.kiro/settings/mcp.json
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@
|
||||||
*
|
*
|
||||||
* 更新说明:
|
* 更新说明:
|
||||||
* - 2026年迁移至 .NET 10 后端
|
* - 2026年迁移至 .NET 10 后端
|
||||||
* - 开发环境:http://localhost:5238
|
* - 开发环境:http://localhost:5238 http://192.168.195.15:2822
|
||||||
* - 测试环境:
|
* - 测试环境:
|
||||||
* - 生产环境:
|
* - 生产环境:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 测试环境配置 - .NET 10 后端
|
// 测试环境配置 - .NET 10 后端
|
||||||
const testing = {
|
const testing = {
|
||||||
baseUrl: 'http://localhost:5238',
|
// baseUrl: 'https://app.zpc-xy.com/honey/api',
|
||||||
|
baseUrl: 'http://192.168.1.24:5238',
|
||||||
imageUrl: 'https://youdas-1308826010.cos.ap-shanghai.myqcloud.com',
|
imageUrl: 'https://youdas-1308826010.cos.ap-shanghai.myqcloud.com',
|
||||||
loginPage: '',
|
loginPage: '',
|
||||||
wxAppId: ''
|
wxAppId: ''
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import RouterManager from '@/common/router.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DetailButton',
|
name: 'DetailButton',
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -77,9 +79,49 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 检查是否已登录
|
||||||
|
checkLogin() {
|
||||||
|
const token = uni.getStorageSync('token');
|
||||||
|
if (!token) {
|
||||||
|
// 保存当前页面路径用于登录后返回
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
const currentPage = pages[pages.length - 1];
|
||||||
|
if (currentPage) {
|
||||||
|
const currentRoute = currentPage.route;
|
||||||
|
const currentParams = currentPage.options || {};
|
||||||
|
|
||||||
|
let redirectPath = '/' + currentRoute;
|
||||||
|
if (Object.keys(currentParams).length > 0) {
|
||||||
|
const paramString = Object.keys(currentParams)
|
||||||
|
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(currentParams[key])}`)
|
||||||
|
.join('&');
|
||||||
|
redirectPath += '?' + paramString;
|
||||||
|
}
|
||||||
|
uni.setStorageSync('redirect', redirectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先登录',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
|
||||||
|
RouterManager.navigateTo('/pages/user/login', {}, 'navigateTo')
|
||||||
|
.catch(err => {
|
||||||
|
console.error('登录页面跳转失败:', err);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
handleButtonClick(num) {
|
handleButtonClick(num) {
|
||||||
console.log(num);
|
console.log(num);
|
||||||
|
|
||||||
|
// 先检查登录状态
|
||||||
|
if (!this.checkLogin()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (num == -1) {
|
if (num == -1) {
|
||||||
|
|
||||||
this.$emit('button-wx-click', [0, buyNum]);
|
this.$emit('button-wx-click', [0, buyNum]);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name" : "友达赏王者",
|
"name" : "友达赏王者",
|
||||||
"appid" : "__UNI__2E6CB39",
|
"appid" : "__UNI__C225F9A",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"versionName" : "1.0.2",
|
"versionName" : "1.0.2",
|
||||||
"versionCode" : 102,
|
"versionCode" : 102,
|
||||||
|
|
@ -140,7 +140,7 @@
|
||||||
"quickapp" : {},
|
"quickapp" : {},
|
||||||
/* 小程序特有相关 */
|
/* 小程序特有相关 */
|
||||||
"mp-weixin" : {
|
"mp-weixin" : {
|
||||||
"appid" : "wxa17265f5fe8374b1",
|
"appid" : "wxbbb54463fe289c5a",
|
||||||
"setting" : {
|
"setting" : {
|
||||||
"urlCheck" : false,
|
"urlCheck" : false,
|
||||||
"es6" : false,
|
"es6" : false,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getDanYeContent } from '@/common/server/config.js';
|
import {
|
||||||
|
getDanYeContent
|
||||||
|
} from '@/common/server/config.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {},
|
components: {},
|
||||||
|
|
@ -80,8 +82,13 @@
|
||||||
// API: danye
|
// API: danye
|
||||||
const res = await getDanYeContent(e);
|
const res = await getDanYeContent(e);
|
||||||
if (res.status === 1) {
|
if (res.status === 1) {
|
||||||
|
if (res.data.content != null) {
|
||||||
|
this.rule = res.data.content;
|
||||||
|
} else {
|
||||||
this.rule = res.data;
|
this.rule = res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using HoneyBox.Core.Interfaces;
|
using HoneyBox.Core.Interfaces;
|
||||||
using HoneyBox.Model.Base;
|
using HoneyBox.Model.Base;
|
||||||
using HoneyBox.Model.Models.Invitation;
|
using HoneyBox.Model.Models.Invitation;
|
||||||
|
|
@ -33,7 +33,7 @@ public class InvitationController : ControllerBase
|
||||||
/// POST /api/invitation
|
/// POST /api/invitation
|
||||||
/// Requirements: 9.1-9.2
|
/// Requirements: 9.1-9.2
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("invitation")]
|
[HttpGet("invitation")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ApiResponse<InvitationInfoResponse>> GetInvitationInfo([FromBody] InvitationInfoRequest? request)
|
public async Task<ApiResponse<InvitationInfoResponse>> GetInvitationInfo([FromBody] InvitationInfoRequest? request)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using HoneyBox.Core.Interfaces;
|
using HoneyBox.Core.Interfaces;
|
||||||
using HoneyBox.Model.Base;
|
using HoneyBox.Model.Base;
|
||||||
using HoneyBox.Model.Models.Asset;
|
using HoneyBox.Model.Models.Asset;
|
||||||
|
|
@ -425,13 +425,13 @@ public class UserController : ControllerBase
|
||||||
/// 获取权益中心信息
|
/// 获取权益中心信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// POST /api/quan_yi
|
/// Get /api/quan_yi
|
||||||
///
|
///
|
||||||
/// 获取当前用户的权益中心信息,包含等级列表和可领取奖品
|
/// 获取当前用户的权益中心信息,包含等级列表和可领取奖品
|
||||||
/// Requirements: 8.1
|
/// Requirements: 8.1
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>权益中心数据</returns>
|
/// <returns>权益中心数据</returns>
|
||||||
[HttpPost("quan_yi")]
|
[HttpGet("quan_yi")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status401Unauthorized)]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"http": {
|
"http": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"applicationUrl": "http://localhost:5238"
|
"applicationUrl": "http://*:5238"
|
||||||
},
|
},
|
||||||
"Container (Dockerfile)": {
|
"Container (Dockerfile)": {
|
||||||
"commandName": "Docker",
|
"commandName": "Docker",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using HoneyBox.Core.Interfaces;
|
using HoneyBox.Core.Interfaces;
|
||||||
using HoneyBox.Model.Data;
|
using HoneyBox.Model.Data;
|
||||||
|
using HoneyBox.Model.Entities;
|
||||||
using HoneyBox.Model.Models;
|
using HoneyBox.Model.Models;
|
||||||
using HoneyBox.Model.Models.Goods;
|
using HoneyBox.Model.Models.Goods;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
@ -1872,15 +1873,38 @@ public class GoodsService : IGoodsService
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
|
// 查询领主的排名记录
|
||||||
|
var kingRank = await _dbContext.GoodsKingRanks
|
||||||
|
.Where(r => r.UserId == goods.KingUserId && r.GoodsId == goodsId)
|
||||||
|
.OrderByDescending(r => r.Id)
|
||||||
|
.Select(r => new { r.Count, r.ZNums, r.OrderListId })
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
// 获取中奖奖品信息
|
||||||
|
string jiangTitle = "";
|
||||||
|
string jiangImg = "";
|
||||||
|
if (kingRank != null && kingRank.OrderListId > 0)
|
||||||
|
{
|
||||||
|
var orderItem = await _dbContext.OrderItems
|
||||||
|
.Where(oi => oi.Id == kingRank.OrderListId)
|
||||||
|
.Select(oi => new { oi.GoodslistTitle, oi.GoodslistImgurl })
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (orderItem != null)
|
||||||
|
{
|
||||||
|
jiangTitle = orderItem.GoodslistTitle ?? "";
|
||||||
|
jiangImg = FormatImageUrl(orderItem.GoodslistImgurl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kingUser = new LingZhuKingUserDto
|
kingUser = new LingZhuKingUserDto
|
||||||
{
|
{
|
||||||
Id = user.Id,
|
Id = user.Id,
|
||||||
Nickname = user.Nickname ?? "",
|
Nickname = user.Nickname ?? "",
|
||||||
HeadImg = FormatImageUrl(user.HeadImg),
|
HeadImg = FormatImageUrl(user.HeadImg),
|
||||||
JiangTitle = "",
|
JiangTitle = jiangTitle,
|
||||||
JiangImg = "",
|
JiangImg = jiangImg,
|
||||||
Count = 0,
|
Count = kingRank?.Count ?? 0,
|
||||||
ZNums = 0
|
ZNums = kingRank?.ZNums ?? 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1913,7 +1937,101 @@ public class GoodsService : IGoodsService
|
||||||
ShangInfo = gi.ShangId.HasValue && shangInfos.TryGetValue(gi.ShangId.Value, out var info) ? info : null
|
ShangInfo = gi.ShangId.HasValue && shangInfos.TryGetValue(gi.ShangId.Value, out var info) ? info : null
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
// 4. 返回结果 (goods_king_rank表未迁移,返回空列表)
|
// 4. 获取挑战者列表或领主记录
|
||||||
|
var listResponse = new PageResponse<LingZhuListItemDto>
|
||||||
|
{
|
||||||
|
Data = new List<LingZhuListItemDto>(),
|
||||||
|
Total = 0,
|
||||||
|
Page = page,
|
||||||
|
PageSize = pageSize,
|
||||||
|
LastPage = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == 1)
|
||||||
|
{
|
||||||
|
// 挑战人数 - 按用户分组,获取每个用户最新的一条记录
|
||||||
|
var distinctUserIds = await _dbContext.GoodsKingRanks
|
||||||
|
.Where(r => r.GoodsId == goodsId)
|
||||||
|
.Select(r => r.UserId)
|
||||||
|
.Distinct()
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var total = distinctUserIds.Count;
|
||||||
|
var lastPage = (int)Math.Ceiling((double)total / pageSize);
|
||||||
|
|
||||||
|
// 获取分页后的用户ID
|
||||||
|
var pagedUserIds = distinctUserIds
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 获取这些用户的最新记录
|
||||||
|
var rankList = new List<GoodsKingRank>();
|
||||||
|
foreach (var userId in pagedUserIds)
|
||||||
|
{
|
||||||
|
var latestRank = await _dbContext.GoodsKingRanks
|
||||||
|
.Where(r => r.GoodsId == goodsId && r.UserId == userId)
|
||||||
|
.OrderByDescending(r => r.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (latestRank != null)
|
||||||
|
{
|
||||||
|
rankList.Add(latestRank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var userList = await _dbContext.Users
|
||||||
|
.Where(u => pagedUserIds.Contains(u.Id))
|
||||||
|
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
|
||||||
|
.ToListAsync();
|
||||||
|
var users = userList.ToDictionary(u => u.Id, u => new { u.Nickname, u.HeadImg });
|
||||||
|
|
||||||
|
listResponse.Data = rankList.Select(r => new LingZhuListItemDto
|
||||||
|
{
|
||||||
|
UserId = r.UserId,
|
||||||
|
Nickname = users.TryGetValue(r.UserId, out var u) ? u.Nickname ?? "" : "",
|
||||||
|
HeadImg = users.TryGetValue(r.UserId, out var u2) ? FormatImageUrl(u2.HeadImg) : "",
|
||||||
|
Addtime = r.Addtime,
|
||||||
|
EndTime = r.EndTime,
|
||||||
|
Time = ""
|
||||||
|
}).ToList();
|
||||||
|
listResponse.Total = total;
|
||||||
|
listResponse.LastPage = lastPage;
|
||||||
|
}
|
||||||
|
else if (type == 2)
|
||||||
|
{
|
||||||
|
// 领主记录
|
||||||
|
var query = _dbContext.GoodsKingRanks
|
||||||
|
.Where(r => r.GoodsId == goodsId);
|
||||||
|
|
||||||
|
var total = await query.CountAsync();
|
||||||
|
var lastPage = (int)Math.Ceiling((double)total / pageSize);
|
||||||
|
|
||||||
|
var rankList = await query
|
||||||
|
.OrderByDescending(r => r.Id)
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var userIds = rankList.Select(r => r.UserId).Distinct().ToList();
|
||||||
|
var userList = await _dbContext.Users
|
||||||
|
.Where(u => userIds.Contains(u.Id))
|
||||||
|
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
|
||||||
|
.ToListAsync();
|
||||||
|
var users = userList.ToDictionary(u => u.Id, u => new { u.Nickname, u.HeadImg });
|
||||||
|
|
||||||
|
listResponse.Data = rankList.Select(r => new LingZhuListItemDto
|
||||||
|
{
|
||||||
|
UserId = r.UserId,
|
||||||
|
Nickname = users.TryGetValue(r.UserId, out var u) ? u.Nickname ?? "" : "",
|
||||||
|
HeadImg = users.TryGetValue(r.UserId, out var u2) ? FormatImageUrl(u2.HeadImg) : "",
|
||||||
|
Addtime = r.Addtime,
|
||||||
|
EndTime = r.EndTime,
|
||||||
|
Time = CalculateTimeSpan(r.Addtime, r.EndTime > 0 ? r.EndTime : (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds())
|
||||||
|
}).ToList();
|
||||||
|
listResponse.Total = total;
|
||||||
|
listResponse.LastPage = lastPage;
|
||||||
|
}
|
||||||
|
|
||||||
return new LingZhuKingResponseDto
|
return new LingZhuKingResponseDto
|
||||||
{
|
{
|
||||||
Goods = new LingZhuGoodsDto
|
Goods = new LingZhuGoodsDto
|
||||||
|
|
@ -1923,15 +2041,27 @@ public class GoodsService : IGoodsService
|
||||||
LingzhuShangId = goods.LingzhuShangId
|
LingzhuShangId = goods.LingzhuShangId
|
||||||
},
|
},
|
||||||
KingUser = kingUser,
|
KingUser = kingUser,
|
||||||
List = new PageResponse<LingZhuListItemDto>
|
List = listResponse,
|
||||||
{
|
|
||||||
Data = new List<LingZhuListItemDto>(),
|
|
||||||
Total = 0,
|
|
||||||
Page = page,
|
|
||||||
PageSize = pageSize,
|
|
||||||
LastPage = 0
|
|
||||||
},
|
|
||||||
LingGoodsList = lingGoodsListDto
|
LingGoodsList = lingGoodsListDto
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算时间间隔的可读字符串
|
||||||
|
/// </summary>
|
||||||
|
private static string CalculateTimeSpan(int startTime, int endTime)
|
||||||
|
{
|
||||||
|
var seconds = endTime - startTime;
|
||||||
|
if (seconds < 0) return "0秒";
|
||||||
|
|
||||||
|
var days = seconds / 86400;
|
||||||
|
var hours = (seconds % 86400) / 3600;
|
||||||
|
var minutes = (seconds % 3600) / 60;
|
||||||
|
var secs = seconds % 60;
|
||||||
|
|
||||||
|
if (days > 0) return $"{days}天{hours}小时";
|
||||||
|
if (hours > 0) return $"{hours}小时{minutes}分钟";
|
||||||
|
if (minutes > 0) return $"{minutes}分钟{secs}秒";
|
||||||
|
return $"{secs}秒";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,16 +65,22 @@ public class WechatService : IWechatService
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 记录配置信息(脱敏)
|
// 从数据库获取微信配置
|
||||||
var maskedAppId = _wechatSettings.AppId?.Length > 8
|
var wechatConfig = await GetWechatSettingFromDbAsync();
|
||||||
? $"{_wechatSettings.AppId.Substring(0, 4)}****{_wechatSettings.AppId.Substring(_wechatSettings.AppId.Length - 4)}"
|
var appId = wechatConfig?.AppId ?? _wechatSettings.AppId;
|
||||||
: "未配置";
|
var appSecret = wechatConfig?.AppSecret ?? _wechatSettings.AppSecret;
|
||||||
var maskedSecret = string.IsNullOrEmpty(_wechatSettings.AppSecret)
|
|
||||||
? "未配置"
|
|
||||||
: $"{_wechatSettings.AppSecret.Substring(0, 4)}****";
|
|
||||||
_logger.LogInformation("[微信登录] 配置信息: AppId={AppId}, AppSecret={AppSecret}", maskedAppId, maskedSecret);
|
|
||||||
|
|
||||||
var url = $"{WechatCodeToSessionUrl}?appid={_wechatSettings.AppId}&secret={_wechatSettings.AppSecret}&js_code={code}&grant_type=authorization_code";
|
// 记录配置信息(脱敏)
|
||||||
|
var maskedAppId = appId?.Length > 8
|
||||||
|
? $"{appId.Substring(0, 4)}****{appId.Substring(appId.Length - 4)}"
|
||||||
|
: "未配置";
|
||||||
|
var maskedSecret = string.IsNullOrEmpty(appSecret)
|
||||||
|
? "未配置"
|
||||||
|
: $"{appSecret.Substring(0, 4)}****";
|
||||||
|
_logger.LogInformation("[微信登录] 配置信息: AppId={AppId}, AppSecret={AppSecret}, 来源={Source}",
|
||||||
|
maskedAppId, maskedSecret, wechatConfig != null ? "数据库" : "appsettings");
|
||||||
|
|
||||||
|
var url = $"{WechatCodeToSessionUrl}?appid={appId}&secret={appSecret}&js_code={code}&grant_type=authorization_code";
|
||||||
|
|
||||||
_logger.LogInformation("[微信登录] 调用微信API: {Url}", WechatCodeToSessionUrl);
|
_logger.LogInformation("[微信登录] 调用微信API: {Url}", WechatCodeToSessionUrl);
|
||||||
|
|
||||||
|
|
@ -590,6 +596,106 @@ public class WechatService : IWechatService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从数据库获取微信小程序配置
|
||||||
|
/// 优先从 miniprogram_setting 读取默认小程序配置,如果没有则回退到 wechat_setting
|
||||||
|
/// </summary>
|
||||||
|
private async Task<WechatSettings?> GetWechatSettingFromDbAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 1. 优先从 miniprogram_setting 读取默认小程序配置
|
||||||
|
var miniprogramConfig = await _dbContext.Configs
|
||||||
|
.Where(c => c.ConfigKey == "miniprogram_setting")
|
||||||
|
.Select(c => c.ConfigValue)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(miniprogramConfig))
|
||||||
|
{
|
||||||
|
var config = JsonSerializer.Deserialize<JsonElement>(miniprogramConfig);
|
||||||
|
|
||||||
|
if (config.TryGetProperty("miniprograms", out var miniprograms) && miniprograms.ValueKind == JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
// 查找默认小程序配置 (is_default = 1)
|
||||||
|
foreach (var mp in miniprograms.EnumerateArray())
|
||||||
|
{
|
||||||
|
var isDefault = mp.TryGetProperty("is_default", out var isDefaultProp) &&
|
||||||
|
(isDefaultProp.ValueKind == JsonValueKind.Number ? isDefaultProp.GetInt32() == 1 : isDefaultProp.GetString() == "1");
|
||||||
|
|
||||||
|
if (isDefault)
|
||||||
|
{
|
||||||
|
var appId = mp.TryGetProperty("appid", out var appIdProp) ? appIdProp.GetString() : null;
|
||||||
|
var appSecret = mp.TryGetProperty("appsecret", out var appSecretProp) ? appSecretProp.GetString() : null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("[微信配置] 从 miniprogram_setting 读取默认小程序配置: AppId={AppId}",
|
||||||
|
appId.Length > 8 ? $"{appId.Substring(0, 4)}****{appId.Substring(appId.Length - 4)}" : appId);
|
||||||
|
return new WechatSettings
|
||||||
|
{
|
||||||
|
AppId = appId,
|
||||||
|
AppSecret = appSecret
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有默认配置,使用第一个小程序配置
|
||||||
|
var firstMp = miniprograms.EnumerateArray().FirstOrDefault();
|
||||||
|
if (firstMp.ValueKind == JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
var appId = firstMp.TryGetProperty("appid", out var appIdProp) ? appIdProp.GetString() : null;
|
||||||
|
var appSecret = firstMp.TryGetProperty("appsecret", out var appSecretProp) ? appSecretProp.GetString() : null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("[微信配置] 从 miniprogram_setting 读取第一个小程序配置: AppId={AppId}",
|
||||||
|
appId.Length > 8 ? $"{appId.Substring(0, 4)}****{appId.Substring(appId.Length - 4)}" : appId);
|
||||||
|
return new WechatSettings
|
||||||
|
{
|
||||||
|
AppId = appId,
|
||||||
|
AppSecret = appSecret
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 回退到 wechat_setting(旧版单一配置)
|
||||||
|
var wechatSettingConfig = await _dbContext.Configs
|
||||||
|
.Where(c => c.ConfigKey == "wechat_setting")
|
||||||
|
.Select(c => c.ConfigValue)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(wechatSettingConfig))
|
||||||
|
{
|
||||||
|
var config = JsonSerializer.Deserialize<JsonElement>(wechatSettingConfig);
|
||||||
|
|
||||||
|
var appId = config.TryGetProperty("appid", out var appIdProp) ? appIdProp.GetString() : null;
|
||||||
|
var appSecret = config.TryGetProperty("appSecret", out var appSecretProp) ? appSecretProp.GetString() : null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("[微信配置] 从 wechat_setting 读取配置: AppId={AppId}",
|
||||||
|
appId.Length > 8 ? $"{appId.Substring(0, 4)}****{appId.Substring(appId.Length - 4)}" : appId);
|
||||||
|
return new WechatSettings
|
||||||
|
{
|
||||||
|
AppId = appId,
|
||||||
|
AppSecret = appSecret
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("[微信配置] 数据库中未找到有效的微信配置");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "[微信配置] 从数据库读取配置失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取商户配置(优先从数据库读取)
|
/// 获取商户配置(优先从数据库读取)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,8 @@ public partial class HoneyBoxDbContext : DbContext
|
||||||
|
|
||||||
public virtual DbSet<EquityLevelPrize> EquityLevelPrizes { get; set; }
|
public virtual DbSet<EquityLevelPrize> EquityLevelPrizes { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<GoodsKingRank> GoodsKingRanks { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
// Connection string is configured in Program.cs via dependency injection
|
// Connection string is configured in Program.cs via dependency injection
|
||||||
|
|
@ -3371,6 +3373,52 @@ public partial class HoneyBoxDbContext : DbContext
|
||||||
.HasConstraintName("fk_equity_level_prizes_equity_levels");
|
.HasConstraintName("fk_equity_level_prizes_equity_levels");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<GoodsKingRank>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id).HasName("pk_goods_king_ranks");
|
||||||
|
|
||||||
|
entity.ToTable("goods_king_ranks", tb => tb.HasComment("领主赏记录表,记录领主占领和挑战信息"));
|
||||||
|
|
||||||
|
entity.HasIndex(e => e.UserId, "ix_goods_king_ranks_user_id");
|
||||||
|
entity.HasIndex(e => e.GoodsId, "ix_goods_king_ranks_goods_id");
|
||||||
|
entity.HasIndex(e => new { e.UserId, e.GoodsId }, "ix_goods_king_ranks_user_goods");
|
||||||
|
|
||||||
|
entity.Property(e => e.Id)
|
||||||
|
.HasComment("主键ID")
|
||||||
|
.HasColumnName("id");
|
||||||
|
entity.Property(e => e.UserId)
|
||||||
|
.HasComment("用户ID(领主)")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
entity.Property(e => e.GoodsId)
|
||||||
|
.HasComment("商品ID")
|
||||||
|
.HasColumnName("goods_id");
|
||||||
|
entity.Property(e => e.Count)
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasComment("占领时的抽奖次数")
|
||||||
|
.HasColumnName("count");
|
||||||
|
entity.Property(e => e.ZNums)
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasComment("被挑战次数")
|
||||||
|
.HasColumnName("z_nums");
|
||||||
|
entity.Property(e => e.Money)
|
||||||
|
.HasDefaultValue(0m)
|
||||||
|
.HasComment("领主收益金额")
|
||||||
|
.HasColumnType("decimal(10, 2)")
|
||||||
|
.HasColumnName("money");
|
||||||
|
entity.Property(e => e.OrderListId)
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasComment("关联的中奖记录ID")
|
||||||
|
.HasColumnName("order_list_id");
|
||||||
|
entity.Property(e => e.Addtime)
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasComment("成为领主的时间戳")
|
||||||
|
.HasColumnName("addtime");
|
||||||
|
entity.Property(e => e.EndTime)
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasComment("结束领主的时间戳")
|
||||||
|
.HasColumnName("end_time");
|
||||||
|
});
|
||||||
|
|
||||||
OnModelCreatingPartial(modelBuilder);
|
OnModelCreatingPartial(modelBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
54
server/HoneyBox/src/HoneyBox.Model/Entities/GoodsKingRank.cs
Normal file
54
server/HoneyBox/src/HoneyBox.Model/Entities/GoodsKingRank.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace HoneyBox.Model.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 领主赏记录表,记录领主占领和挑战信息
|
||||||
|
/// </summary>
|
||||||
|
public partial class GoodsKingRank
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 主键ID
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户ID(领主)
|
||||||
|
/// </summary>
|
||||||
|
public int UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 商品ID
|
||||||
|
/// </summary>
|
||||||
|
public int GoodsId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 占领时的抽奖次数(多少发晋升领主)
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 被挑战次数(其他人抽奖次数)
|
||||||
|
/// </summary>
|
||||||
|
public int ZNums { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 领主收益金额
|
||||||
|
/// </summary>
|
||||||
|
public decimal Money { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关联的中奖记录ID
|
||||||
|
/// </summary>
|
||||||
|
public int OrderListId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成为领主的时间戳
|
||||||
|
/// </summary>
|
||||||
|
public int Addtime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束领主的时间戳(0表示当前领主)
|
||||||
|
/// </summary>
|
||||||
|
public int EndTime { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -531,7 +531,7 @@ public class GoodsListItemDto
|
||||||
[JsonPropertyName("surplus_stock")]
|
[JsonPropertyName("surplus_stock")]
|
||||||
public int SurplusStock { get; set; }
|
public int SurplusStock { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("img_url")]
|
[JsonPropertyName("imgurl")]
|
||||||
public string ImgUrl { get; set; } = string.Empty;
|
public string ImgUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("goods_type")]
|
[JsonPropertyName("goods_type")]
|
||||||
|
|
|
||||||
48
server/scripts/create_goods_king_ranks.sql
Normal file
48
server/scripts/create_goods_king_ranks.sql
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
-- 创建 goods_king_ranks 表
|
||||||
|
-- 领主赏记录表,记录领主占领和挑战信息
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='goods_king_ranks' AND xtype='U')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE goods_king_ranks (
|
||||||
|
id INT IDENTITY(1,1) NOT NULL,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
goods_id INT NOT NULL,
|
||||||
|
count INT NOT NULL DEFAULT 0,
|
||||||
|
z_nums INT NOT NULL DEFAULT 0,
|
||||||
|
money DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||||
|
order_list_id INT NOT NULL DEFAULT 0,
|
||||||
|
addtime INT NOT NULL DEFAULT 0,
|
||||||
|
end_time INT NOT NULL DEFAULT 0,
|
||||||
|
CONSTRAINT pk_goods_king_ranks PRIMARY KEY CLUSTERED (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 创建索引
|
||||||
|
CREATE INDEX ix_goods_king_ranks_user_id ON goods_king_ranks(user_id);
|
||||||
|
CREATE INDEX ix_goods_king_ranks_goods_id ON goods_king_ranks(goods_id);
|
||||||
|
CREATE INDEX ix_goods_king_ranks_user_goods ON goods_king_ranks(user_id, goods_id);
|
||||||
|
|
||||||
|
-- 添加表注释
|
||||||
|
EXEC sys.sp_addextendedproperty
|
||||||
|
@name=N'MS_Description',
|
||||||
|
@value=N'领主赏记录表,记录领主占领和挑战信息',
|
||||||
|
@level0type=N'SCHEMA', @level0name=N'dbo',
|
||||||
|
@level1type=N'TABLE', @level1name=N'goods_king_ranks';
|
||||||
|
|
||||||
|
-- 添加列注释
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'主键ID', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'id';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用户ID(领主)', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'user_id';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'商品ID', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'goods_id';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'占领时的抽奖次数', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'count';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'被挑战次数', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'z_nums';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'领主收益金额', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'money';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'关联的中奖记录ID', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'order_list_id';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'成为领主的时间戳', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'addtime';
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'结束领主的时间戳', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks', @level2type=N'COLUMN', @level2name=N'end_time';
|
||||||
|
|
||||||
|
PRINT '表 goods_king_ranks 创建成功';
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
BEGIN
|
||||||
|
PRINT '表 goods_king_ranks 已存在';
|
||||||
|
END
|
||||||
|
GO
|
||||||
197
server/scripts/migrate_goods_king_rank.js
Normal file
197
server/scripts/migrate_goods_king_rank.js
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
/**
|
||||||
|
* 领主赏记录表迁移脚本
|
||||||
|
* 从 MySQL goods_king_rank 表迁移到 SQL Server goods_king_ranks 表
|
||||||
|
*/
|
||||||
|
|
||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
const sql = require('mssql');
|
||||||
|
|
||||||
|
// MySQL 配置
|
||||||
|
const mysqlConfig = {
|
||||||
|
host: '192.168.195.16',
|
||||||
|
port: 1887,
|
||||||
|
user: 'root',
|
||||||
|
password: 'Dbt@com@123',
|
||||||
|
database: 'youdas',
|
||||||
|
charset: 'utf8mb4'
|
||||||
|
};
|
||||||
|
|
||||||
|
// SQL Server 配置
|
||||||
|
const sqlServerConfig = {
|
||||||
|
server: '192.168.195.15',
|
||||||
|
port: 1433,
|
||||||
|
user: 'sa',
|
||||||
|
password: 'Dbt@com@123',
|
||||||
|
database: 'honey_box',
|
||||||
|
options: {
|
||||||
|
encrypt: false,
|
||||||
|
trustServerCertificate: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量大小
|
||||||
|
const BATCH_SIZE = 1000;
|
||||||
|
|
||||||
|
async function createTable(pool) {
|
||||||
|
console.log('创建 goods_king_ranks 表...');
|
||||||
|
|
||||||
|
const createTableSql = `
|
||||||
|
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='goods_king_ranks' AND xtype='U')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE goods_king_ranks (
|
||||||
|
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
goods_id INT NOT NULL,
|
||||||
|
count INT NOT NULL DEFAULT 0,
|
||||||
|
z_nums INT NOT NULL DEFAULT 0,
|
||||||
|
money DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||||
|
order_list_id INT NOT NULL DEFAULT 0,
|
||||||
|
addtime INT NOT NULL DEFAULT 0,
|
||||||
|
end_time INT NOT NULL DEFAULT 0,
|
||||||
|
CONSTRAINT pk_goods_king_ranks PRIMARY KEY CLUSTERED (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX ix_goods_king_ranks_user_id ON goods_king_ranks(user_id);
|
||||||
|
CREATE INDEX ix_goods_king_ranks_goods_id ON goods_king_ranks(goods_id);
|
||||||
|
CREATE INDEX ix_goods_king_ranks_user_goods ON goods_king_ranks(user_id, goods_id);
|
||||||
|
|
||||||
|
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'领主赏记录表,记录领主占领和挑战信息', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'goods_king_ranks';
|
||||||
|
END
|
||||||
|
`;
|
||||||
|
|
||||||
|
await pool.request().query(createTableSql);
|
||||||
|
console.log('表创建完成');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSourceCount(mysqlConn) {
|
||||||
|
const [rows] = await mysqlConn.execute('SELECT COUNT(*) as count FROM goods_king_rank');
|
||||||
|
return rows[0].count;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTargetCount(pool) {
|
||||||
|
const result = await pool.request().query('SELECT COUNT(*) as count FROM goods_king_ranks');
|
||||||
|
return result.recordset[0].count;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migrateData(mysqlConn, sqlPool) {
|
||||||
|
const sourceCount = await getSourceCount(mysqlConn);
|
||||||
|
console.log(`源表记录数: ${sourceCount}`);
|
||||||
|
|
||||||
|
if (sourceCount === 0) {
|
||||||
|
console.log('源表无数据,跳过迁移');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空目标表
|
||||||
|
console.log('清空目标表...');
|
||||||
|
await sqlPool.request().query('TRUNCATE TABLE goods_king_ranks');
|
||||||
|
|
||||||
|
// 开启 IDENTITY_INSERT
|
||||||
|
await sqlPool.request().query('SET IDENTITY_INSERT goods_king_ranks ON');
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
let migratedCount = 0;
|
||||||
|
|
||||||
|
while (offset < sourceCount) {
|
||||||
|
console.log(`迁移进度: ${offset}/${sourceCount}`);
|
||||||
|
|
||||||
|
// 从 MySQL 读取数据
|
||||||
|
const [rows] = await mysqlConn.execute(
|
||||||
|
`SELECT id, user_id, goods_id, count, z_nums, money, order_list_id, addtime, end_time
|
||||||
|
FROM goods_king_rank
|
||||||
|
ORDER BY id
|
||||||
|
LIMIT ? OFFSET ?`,
|
||||||
|
[BATCH_SIZE, offset]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rows.length === 0) break;
|
||||||
|
|
||||||
|
// 批量插入到 SQL Server
|
||||||
|
const table = new sql.Table('goods_king_ranks');
|
||||||
|
table.create = false;
|
||||||
|
table.columns.add('id', sql.Int, { nullable: false });
|
||||||
|
table.columns.add('user_id', sql.Int, { nullable: false });
|
||||||
|
table.columns.add('goods_id', sql.Int, { nullable: false });
|
||||||
|
table.columns.add('count', sql.Int, { nullable: false });
|
||||||
|
table.columns.add('z_nums', sql.Int, { nullable: false });
|
||||||
|
table.columns.add('money', sql.Decimal(10, 2), { nullable: false });
|
||||||
|
table.columns.add('order_list_id', sql.Int, { nullable: false });
|
||||||
|
table.columns.add('addtime', sql.Int, { nullable: false });
|
||||||
|
table.columns.add('end_time', sql.Int, { nullable: false });
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
table.rows.add(
|
||||||
|
row.id,
|
||||||
|
row.user_id || 0,
|
||||||
|
row.goods_id || 0,
|
||||||
|
row.count || 0,
|
||||||
|
row.z_nums || 0,
|
||||||
|
row.money || 0,
|
||||||
|
row.order_list_id || 0,
|
||||||
|
row.addtime || 0,
|
||||||
|
row.end_time || 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = sqlPool.request();
|
||||||
|
await request.bulk(table);
|
||||||
|
|
||||||
|
migratedCount += rows.length;
|
||||||
|
offset += BATCH_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭 IDENTITY_INSERT
|
||||||
|
await sqlPool.request().query('SET IDENTITY_INSERT goods_king_ranks OFF');
|
||||||
|
|
||||||
|
// 重置自增序列
|
||||||
|
const maxIdResult = await sqlPool.request().query('SELECT MAX(id) as maxId FROM goods_king_ranks');
|
||||||
|
const maxId = maxIdResult.recordset[0].maxId || 0;
|
||||||
|
await sqlPool.request().query(`DBCC CHECKIDENT ('goods_king_ranks', RESEED, ${maxId})`);
|
||||||
|
|
||||||
|
console.log(`迁移完成,共迁移 ${migratedCount} 条记录`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validate(mysqlConn, sqlPool) {
|
||||||
|
console.log('\n验证迁移结果...');
|
||||||
|
|
||||||
|
const sourceCount = await getSourceCount(mysqlConn);
|
||||||
|
const targetCount = await getTargetCount(sqlPool);
|
||||||
|
|
||||||
|
console.log(`源表记录数: ${sourceCount}`);
|
||||||
|
console.log(`目标表记录数: ${targetCount}`);
|
||||||
|
|
||||||
|
if (sourceCount === targetCount) {
|
||||||
|
console.log('✓ 记录数一致');
|
||||||
|
} else {
|
||||||
|
console.log('✗ 记录数不一致!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let mysqlConn = null;
|
||||||
|
let sqlPool = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('连接 MySQL...');
|
||||||
|
mysqlConn = await mysql.createConnection(mysqlConfig);
|
||||||
|
console.log('MySQL 连接成功');
|
||||||
|
|
||||||
|
console.log('连接 SQL Server...');
|
||||||
|
sqlPool = await sql.connect(sqlServerConfig);
|
||||||
|
console.log('SQL Server 连接成功');
|
||||||
|
|
||||||
|
await createTable(sqlPool);
|
||||||
|
await migrateData(mysqlConn, sqlPool);
|
||||||
|
await validate(mysqlConn, sqlPool);
|
||||||
|
|
||||||
|
console.log('\n迁移任务完成!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('迁移失败:', error);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
if (mysqlConn) await mysqlConn.end();
|
||||||
|
if (sqlPool) await sqlPool.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
Loading…
Reference in New Issue
Block a user