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
|
||||
.pnp.*
|
||||
server/C#/HoneyBox/src/HoneyBox.Api/bin/*
|
||||
.kiro/settings/mcp.json
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@
|
|||
*
|
||||
* 更新说明:
|
||||
* - 2026年迁移至 .NET 10 后端
|
||||
* - 开发环境:http://localhost:5238
|
||||
* - 开发环境:http://localhost:5238 http://192.168.195.15:2822
|
||||
* - 测试环境:
|
||||
* - 生产环境:
|
||||
*/
|
||||
|
||||
// 测试环境配置 - .NET 10 后端
|
||||
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',
|
||||
loginPage: '',
|
||||
wxAppId: ''
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import RouterManager from '@/common/router.js'
|
||||
|
||||
export default {
|
||||
name: 'DetailButton',
|
||||
props: {
|
||||
|
|
@ -77,9 +79,49 @@ export default {
|
|||
}
|
||||
},
|
||||
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) {
|
||||
console.log(num);
|
||||
|
||||
// 先检查登录状态
|
||||
if (!this.checkLogin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (num == -1) {
|
||||
|
||||
this.$emit('button-wx-click', [0, buyNum]);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name" : "友达赏王者",
|
||||
"appid" : "__UNI__2E6CB39",
|
||||
"appid" : "__UNI__C225F9A",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.2",
|
||||
"versionCode" : 102,
|
||||
|
|
@ -140,7 +140,7 @@
|
|||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "wxa17265f5fe8374b1",
|
||||
"appid" : "wxbbb54463fe289c5a",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"es6" : false,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { getDanYeContent } from '@/common/server/config.js';
|
||||
import {
|
||||
getDanYeContent
|
||||
} from '@/common/server/config.js';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
|
|
@ -80,7 +82,12 @@
|
|||
// API: danye
|
||||
const res = await getDanYeContent(e);
|
||||
if (res.status === 1) {
|
||||
this.rule = res.data;
|
||||
if (res.data.content != null) {
|
||||
this.rule = res.data.content;
|
||||
} else {
|
||||
this.rule = res.data;
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Security.Claims;
|
||||
using System.Security.Claims;
|
||||
using HoneyBox.Core.Interfaces;
|
||||
using HoneyBox.Model.Base;
|
||||
using HoneyBox.Model.Models.Invitation;
|
||||
|
|
@ -33,7 +33,7 @@ public class InvitationController : ControllerBase
|
|||
/// POST /api/invitation
|
||||
/// Requirements: 9.1-9.2
|
||||
/// </summary>
|
||||
[HttpPost("invitation")]
|
||||
[HttpGet("invitation")]
|
||||
[Authorize]
|
||||
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.Model.Base;
|
||||
using HoneyBox.Model.Models.Asset;
|
||||
|
|
@ -425,13 +425,13 @@ public class UserController : ControllerBase
|
|||
/// 获取权益中心信息
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/quan_yi
|
||||
/// Get /api/quan_yi
|
||||
///
|
||||
/// 获取当前用户的权益中心信息,包含等级列表和可领取奖品
|
||||
/// Requirements: 8.1
|
||||
/// </remarks>
|
||||
/// <returns>权益中心数据</returns>
|
||||
[HttpPost("quan_yi")]
|
||||
[HttpGet("quan_yi")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status401Unauthorized)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5238"
|
||||
"applicationUrl": "http://*:5238"
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
"commandName": "Docker",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using HoneyBox.Core.Interfaces;
|
||||
using HoneyBox.Model.Data;
|
||||
using HoneyBox.Model.Entities;
|
||||
using HoneyBox.Model.Models;
|
||||
using HoneyBox.Model.Models.Goods;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
@ -1872,15 +1873,38 @@ public class GoodsService : IGoodsService
|
|||
|
||||
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
|
||||
{
|
||||
Id = user.Id,
|
||||
Nickname = user.Nickname ?? "",
|
||||
HeadImg = FormatImageUrl(user.HeadImg),
|
||||
JiangTitle = "",
|
||||
JiangImg = "",
|
||||
Count = 0,
|
||||
ZNums = 0
|
||||
JiangTitle = jiangTitle,
|
||||
JiangImg = jiangImg,
|
||||
Count = kingRank?.Count ?? 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
|
||||
}).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
|
||||
{
|
||||
Goods = new LingZhuGoodsDto
|
||||
|
|
@ -1923,15 +2041,27 @@ public class GoodsService : IGoodsService
|
|||
LingzhuShangId = goods.LingzhuShangId
|
||||
},
|
||||
KingUser = kingUser,
|
||||
List = new PageResponse<LingZhuListItemDto>
|
||||
{
|
||||
Data = new List<LingZhuListItemDto>(),
|
||||
Total = 0,
|
||||
Page = page,
|
||||
PageSize = pageSize,
|
||||
LastPage = 0
|
||||
},
|
||||
List = listResponse,
|
||||
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
|
||||
{
|
||||
// 记录配置信息(脱敏)
|
||||
var maskedAppId = _wechatSettings.AppId?.Length > 8
|
||||
? $"{_wechatSettings.AppId.Substring(0, 4)}****{_wechatSettings.AppId.Substring(_wechatSettings.AppId.Length - 4)}"
|
||||
: "未配置";
|
||||
var maskedSecret = string.IsNullOrEmpty(_wechatSettings.AppSecret)
|
||||
? "未配置"
|
||||
: $"{_wechatSettings.AppSecret.Substring(0, 4)}****";
|
||||
_logger.LogInformation("[微信登录] 配置信息: AppId={AppId}, AppSecret={AppSecret}", maskedAppId, maskedSecret);
|
||||
// 从数据库获取微信配置
|
||||
var wechatConfig = await GetWechatSettingFromDbAsync();
|
||||
var appId = wechatConfig?.AppId ?? _wechatSettings.AppId;
|
||||
var appSecret = wechatConfig?.AppSecret ?? _wechatSettings.AppSecret;
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ public partial class HoneyBoxDbContext : DbContext
|
|||
|
||||
public virtual DbSet<EquityLevelPrize> EquityLevelPrizes { get; set; }
|
||||
|
||||
public virtual DbSet<GoodsKingRank> GoodsKingRanks { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
// 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");
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
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")]
|
||||
public int SurplusStock { get; set; }
|
||||
|
||||
[JsonPropertyName("img_url")]
|
||||
[JsonPropertyName("imgurl")]
|
||||
public string ImgUrl { get; set; } = string.Empty;
|
||||
|
||||
[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