提交
This commit is contained in:
280
csv_output/quest.csv
Normal file
280
csv_output/quest.csv
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
主键,分类 1主线2日常3通行证4修炼任务5周常6公会7委托8称号9成就10七日任务,下一个任务,描述,子类型(见页签desc),任务目标,不同类型自行解析,打开界面类型,打开界面描述,奖励(类型_id_数量),每日任务活跃度,自动领奖(0是否,1是是),额外数据
|
||||||
|
int,int,int,string,int,string,int,string,string[],int,int,string
|
||||||
|
id,category,next,desc,type,target,openUI_type,openUI_desc,rewards,score,auto,extra
|
||||||
|
10001,1,10002,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_2400,item_10000002_50",0,0,null
|
||||||
|
10002,1,10003,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_2835,item_10000002_100",0,0,null
|
||||||
|
10003,1,10004,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_3279,item_10000002_150",0,0,null
|
||||||
|
10004,1,10005,招募2次。,1,2,2,UI_DrawCardPanel,"item_10000001_3702,item_10000002_200",0,0,null
|
||||||
|
10005,1,10006,消耗2000金币。,9,2000,3,UI_ShopPanel,"item_10000001_4116,item_10000002_250",0,0,null
|
||||||
|
10006,1,10007,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_4528,item_10000002_300",0,0,null
|
||||||
|
10007,1,10008,招募3次。,1,3,2,UI_DrawCardPanel,"item_10000001_4950,item_10000002_350",0,0,null
|
||||||
|
10008,1,10009,消耗4000金币。,9,4000,3,UI_ShopPanel,"item_10000001_5390,item_10000002_400",0,0,null
|
||||||
|
10009,1,10010,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_5791,item_10000002_450",0,0,null
|
||||||
|
10010,1,10011,招募4次。,1,4,2,UI_DrawCardPanel,"item_10000001_6195,item_10000002_500",0,0,null
|
||||||
|
10011,1,10012,消耗8000金币。,9,8000,3,UI_ShopPanel,"item_10000001_6645,item_10000002_550",0,0,null
|
||||||
|
10012,1,10013,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_7080,item_10000002_600",0,0,null
|
||||||
|
10013,1,10014,招募5次。,1,5,2,UI_DrawCardPanel,"item_10000001_7523,item_10000002_650",0,0,null
|
||||||
|
10014,1,10015,消耗1万金币。,9,16000,3,UI_ShopPanel,"item_10000001_7924,item_10000002_700",0,0,null
|
||||||
|
10015,1,10016,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_8349,item_10000002_750",0,0,null
|
||||||
|
10016,1,10017,招募6次。,1,6,2,UI_DrawCardPanel,"item_10000001_8750,item_10000002_800",0,0,null
|
||||||
|
10017,1,10018,消耗3万金币。,9,32000,3,UI_ShopPanel,"item_10000001_9199,item_10000002_850",0,0,null
|
||||||
|
10018,1,10019,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_9613,item_10000002_900",0,0,null
|
||||||
|
10019,1,10020,招募7次。,1,7,2,UI_DrawCardPanel,"item_10000001_10042,item_10000002_950",0,0,null
|
||||||
|
10020,1,10021,消耗5万金币。,9,50000,3,UI_ShopPanel,"item_10000001_10484,item_10000002_1000",0,0,null
|
||||||
|
10021,1,10022,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_10922,item_10000002_1050",0,0,null
|
||||||
|
10022,1,10023,招募8次。,1,8,2,UI_DrawCardPanel,"item_10000001_11360,item_10000002_1100",0,0,null
|
||||||
|
10023,1,10024,消耗8万金币。,9,80000,3,UI_ShopPanel,"item_10000001_11795,item_10000002_1150",0,0,null
|
||||||
|
10024,1,10025,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_12204,item_10000002_1200",0,0,null
|
||||||
|
10025,1,10026,招募9次。,1,9,2,UI_DrawCardPanel,"item_10000001_12614,item_10000002_1250",0,0,null
|
||||||
|
10026,1,10027,消耗10万金币。,9,100000,3,UI_ShopPanel,"item_10000001_13025,item_10000002_1300",0,0,null
|
||||||
|
10027,1,10028,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_13435,item_10000002_1350",0,0,null
|
||||||
|
10028,1,10029,招募10次。,1,10,2,UI_DrawCardPanel,"item_10000001_13840,item_10000002_1400",0,0,null
|
||||||
|
10029,1,10030,消耗10万金币。,9,100000,3,UI_ShopPanel,"item_10000001_14252,item_10000002_1450",0,0,null
|
||||||
|
10030,1,10031,通过1关主线关卡。,6,1,1,UI_MainPanel,"item_10000001_14693,item_10000002_1500",0,0,null
|
||||||
|
10031,1,10032,挑战3次副本。,13,3,4,UI_FBPanel,"item_10000001_15110,item_10000002_1550",0,0,null
|
||||||
|
10032,1,10033,消耗12万金币。,9,120000,3,UI_ShopPanel,"item_10000001_15530,item_10000002_1600",0,0,null
|
||||||
|
10033,1,10034,挑战4次副本。,13,4,4,UI_FBPanel,"item_10000001_15942,item_10000002_1650",0,0,null
|
||||||
|
10034,1,10035,消耗15万金币。,9,150000,3,UI_ShopPanel,"item_10000001_16357,item_10000002_1700",0,0,null
|
||||||
|
10035,1,10036,挑战5次副本。,13,5,4,UI_FBPanel,"item_10000001_16768,item_10000002_1750",0,0,null
|
||||||
|
10036,1,10037,消耗20万金币。,9,200000,3,UI_ShopPanel,"item_10000001_17188,item_10000002_1800",0,0,null
|
||||||
|
10037,1,10038,挑战6次副本。,13,6,4,UI_FBPanel,"item_10000001_17608,item_10000002_1850",0,0,null
|
||||||
|
10038,1,10039,消耗25万金币。,9,250000,3,UI_ShopPanel,"item_10000001_18050,item_10000002_1900",0,0,null
|
||||||
|
10039,1,10040,挑战7次副本。,13,7,4,UI_FBPanel,"item_10000001_18483,item_10000002_1950",0,0,null
|
||||||
|
10040,1,10041,消耗30万金币。,9,300000,3,UI_ShopPanel,"item_10000001_18925,item_10000002_2000",0,0,null
|
||||||
|
10041,1,10042,通过3关主线关卡。,6,3,1,UI_MainPanel,"item_10000001_19369,item_10000002_2050",0,0,null
|
||||||
|
10042,1,10043,消耗35万金币。,9,350000,3,UI_ShopPanel,"item_10000001_19790,item_10000002_2100",0,0,null
|
||||||
|
10043,1,10044,通过4关主线关卡。,6,4,1,UI_MainPanel,"item_10000001_20229,item_10000002_2150",0,0,null
|
||||||
|
10044,1,10045,消耗40万金币。,9,400000,3,UI_ShopPanel,"item_10000001_20677,item_10000002_2200",0,0,null
|
||||||
|
10045,1,10046,通过5关主线关卡。,6,5,1,UI_MainPanel,"item_10000001_21106,item_10000002_2250",0,0,null
|
||||||
|
10046,1,10047,消耗40万金币。,9,400000,3,UI_ShopPanel,"item_10000001_21539,item_10000002_2300",0,0,null
|
||||||
|
10047,1,10048,通过6关主线关卡。,6,6,1,UI_MainPanel,"item_10000001_21950,item_10000002_2350",0,0,null
|
||||||
|
10048,1,10049,消耗45万金币。,9,450000,3,UI_ShopPanel,"item_10000001_22368,item_10000002_2400",0,0,null
|
||||||
|
10049,1,10050,通过7关主线关卡。,6,7,1,UI_MainPanel,"item_10000001_22793,item_10000002_2450",0,0,null
|
||||||
|
10050,1,10051,消耗50万金币。,9,500000,3,UI_ShopPanel,"item_10000001_23199,item_10000002_2500",0,0,null
|
||||||
|
10051,1,10052,通过8关主线关卡。,6,8,1,UI_MainPanel,"item_10000001_23604,item_10000002_2550",0,0,null
|
||||||
|
10052,1,10053,消耗55万金币。,9,550000,3,UI_ShopPanel,"item_10000001_24024,item_10000002_2600",0,0,null
|
||||||
|
10053,1,10054,通过9关主线关卡。,6,9,1,UI_MainPanel,"item_10000001_24428,item_10000002_2650",0,0,null
|
||||||
|
10054,1,10055,消耗60万金币。,9,600000,3,UI_ShopPanel,"item_10000001_24845,item_10000002_2700",0,0,null
|
||||||
|
10055,1,10056,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_25284,item_10000002_2750",0,0,null
|
||||||
|
10056,1,10057,消耗65万金币。,9,650000,3,UI_ShopPanel,"item_10000001_25685,item_10000002_2800",0,0,null
|
||||||
|
10057,1,10058,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_26090,item_10000002_2850",0,0,null
|
||||||
|
10058,1,10059,消耗70万金币。,9,700000,3,UI_ShopPanel,"item_10000001_26500,item_10000002_2900",0,0,null
|
||||||
|
10059,1,10060,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_26929,item_10000002_2950",0,0,null
|
||||||
|
10060,1,10061,消耗75万金币。,9,750000,3,UI_ShopPanel,"item_10000001_27353,item_10000002_3000",0,0,null
|
||||||
|
10061,1,10062,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_27764,item_10000002_3050",0,0,null
|
||||||
|
10062,1,10063,消耗80万金币。,9,800000,3,UI_ShopPanel,"item_10000001_28182,item_10000002_3100",0,0,null
|
||||||
|
10063,1,10064,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_28613,item_10000002_3150",0,0,null
|
||||||
|
10064,1,10065,消耗85万金币。,9,850000,3,UI_ShopPanel,"item_10000001_29055,item_10000002_3200",0,0,null
|
||||||
|
10065,1,10066,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_29479,item_10000002_3250",0,0,null
|
||||||
|
10066,1,10067,消耗90万金币。,9,900000,3,UI_ShopPanel,"item_10000001_29896,item_10000002_3300",0,0,null
|
||||||
|
10067,1,10068,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_30332,item_10000002_3350",0,0,null
|
||||||
|
10068,1,10069,消耗95万金币。,9,950000,3,UI_ShopPanel,"item_10000001_30762,item_10000002_3400",0,0,null
|
||||||
|
10069,1,10070,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_31165,item_10000002_3450",0,0,null
|
||||||
|
10070,1,10071,消耗100万金币。,9,1000000,3,UI_ShopPanel,"item_10000001_31568,item_10000002_3500",0,0,null
|
||||||
|
10071,1,10072,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_32010,item_10000002_3550",0,0,null
|
||||||
|
10072,1,10073,消耗110万金币。,9,1100000,3,UI_ShopPanel,"item_10000001_32438,item_10000002_3600",0,0,null
|
||||||
|
10073,1,10074,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_32848,item_10000002_3650",0,0,null
|
||||||
|
10074,1,10075,消耗120万金币。,9,1200000,3,UI_ShopPanel,"item_10000001_33283,item_10000002_3700",0,0,null
|
||||||
|
10075,1,10076,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_33707,item_10000002_3750",0,0,null
|
||||||
|
10076,1,10077,消耗130万金币。,9,1300000,3,UI_ShopPanel,"item_10000001_34110,item_10000002_3800",0,0,null
|
||||||
|
10077,1,10078,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_34543,item_10000002_3850",0,0,null
|
||||||
|
10078,1,10079,消耗140万金币。,9,1400000,3,UI_ShopPanel,"item_10000001_34992,item_10000002_3900",0,0,null
|
||||||
|
10079,1,10080,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_35427,item_10000002_3950",0,0,null
|
||||||
|
10080,1,10081,消耗150万金币。,9,1500000,3,UI_ShopPanel,"item_10000001_35833,item_10000002_4000",0,0,null
|
||||||
|
10081,1,10082,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_36271,item_10000002_4050",0,0,null
|
||||||
|
10082,1,10083,消耗160万金币。,9,1600000,3,UI_ShopPanel,"item_10000001_36716,item_10000002_4100",0,0,null
|
||||||
|
10083,1,10084,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_37154,item_10000002_4150",0,0,null
|
||||||
|
10084,1,10085,消耗170万金币。,9,1700000,3,UI_ShopPanel,"item_10000001_37600,item_10000002_4200",0,0,null
|
||||||
|
10085,1,10086,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_38043,item_10000002_4250",0,0,null
|
||||||
|
10086,1,10087,消耗180万金币。,9,1800000,3,UI_ShopPanel,"item_10000001_38466,item_10000002_4300",0,0,null
|
||||||
|
10087,1,10088,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_38906,item_10000002_4350",0,0,null
|
||||||
|
10088,1,10089,消耗190万金币。,9,1900000,3,UI_ShopPanel,"item_10000001_39336,item_10000002_4400",0,0,null
|
||||||
|
10089,1,10090,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_39771,item_10000002_4450",0,0,null
|
||||||
|
10090,1,10091,消耗200万金币。,9,2000000,3,UI_ShopPanel,"item_10000001_40202,item_10000002_4500",0,0,null
|
||||||
|
10091,1,10092,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_40621,item_10000002_4550",0,0,null
|
||||||
|
10092,1,10093,消耗220万金币。,9,2200000,3,UI_ShopPanel,"item_10000001_41046,item_10000002_4600",0,0,null
|
||||||
|
10093,1,10094,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_41459,item_10000002_4650",0,0,null
|
||||||
|
10094,1,10095,消耗240万金币。,9,2400000,3,UI_ShopPanel,"item_10000001_41905,item_10000002_4700",0,0,null
|
||||||
|
10095,1,10096,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_42335,item_10000002_4750",0,0,null
|
||||||
|
10096,1,10097,消耗260万金币。,9,2600000,3,UI_ShopPanel,"item_10000001_42768,item_10000002_4800",0,0,null
|
||||||
|
10097,1,10098,通过10关主线关卡。,6,10,1,UI_MainPanel,"item_10000001_43180,item_10000002_4850",0,0,null
|
||||||
|
10098,1,10099,消耗280万金币。,9,2800000,3,UI_ShopPanel,"item_10000001_43596,item_10000002_4900",0,0,null
|
||||||
|
10099,1,10100,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_44022,item_10000002_4950",0,0,null
|
||||||
|
10100,1,10101,消耗300万金币。,9,3000000,3,UI_ShopPanel,"item_10000001_44427,item_10000002_5000",0,0,null
|
||||||
|
10101,1,10102,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_44833,item_10000002_5050",0,0,null
|
||||||
|
10102,1,10103,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_45235,item_10000002_5100",0,0,null
|
||||||
|
10103,1,10104,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_45655,item_10000002_5150",0,0,null
|
||||||
|
10104,1,10105,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_46067,item_10000002_5200",0,0,null
|
||||||
|
10105,1,10106,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_46517,item_10000002_5250",0,0,null
|
||||||
|
10106,1,10107,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_46917,item_10000002_5300",0,0,null
|
||||||
|
10107,1,10108,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_47331,item_10000002_5350",0,0,null
|
||||||
|
10108,1,10109,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_47750,item_10000002_5400",0,0,null
|
||||||
|
10109,1,10110,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_48187,item_10000002_5450",0,0,null
|
||||||
|
10110,1,10111,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_48597,item_10000002_5500",0,0,null
|
||||||
|
10111,1,10112,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_49021,item_10000002_5550",0,0,null
|
||||||
|
10112,1,10113,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_49453,item_10000002_5600",0,0,null
|
||||||
|
10113,1,10114,通过20关主线关卡。,6,20,1,UI_MainPanel,"item_10000001_49881,item_10000002_5650",0,0,null
|
||||||
|
10114,1,10115,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_50297,item_10000002_5700",0,0,null
|
||||||
|
10115,1,0,消耗400万金币。,9,4000000,3,UI_ShopPanel,"item_10000001_50726,item_10000002_5750",0,0,null
|
||||||
|
20001,2,0,招募3次。,1,3,2,UI_DrawCardPanel,item_20000001_1,20,0,null
|
||||||
|
20002,2,0,挑战2次副本。,13,2,4,UI_FBPanel,item_30000006_2,20,0,null
|
||||||
|
20003,2,0,商店购买3次。,8,3,3,UI_ShopPanel,item_20000017_1000,20,0,null
|
||||||
|
20004,2,0,通过1关主线关卡。,6,1,1,UI_MainPanel,item_30000008_1,20,0,null
|
||||||
|
20005,2,0,击败7波敌人。,7,7,1,UI_MainPanel,item_30000005_2,20,0,null
|
||||||
|
20006,2,0,消耗3000金币。,9,3000,3,UI_ShopPanel,item_30000007_2,20,0,null
|
||||||
|
20007,2,0,登录1天。,11,1,1,UI_MainPanel,item_10000002_150,20,0,null
|
||||||
|
20008,2,0,领取3次游历奖励。,16,3,5,UI_IdlePanel,item_10000002_150,20,0,null
|
||||||
|
20009,2,0,世界频道发言1次。,17,1,6,UI_ChatPanel,item_10000003_2000,20,0,null
|
||||||
|
30001,3,0,击败7波敌人。,7,7,1,UI_MainPanel,item_10000001_233,0,0,null
|
||||||
|
30002,3,0,击败14波敌人。,7,14,1,UI_MainPanel,item_10000001_234,0,0,null
|
||||||
|
30003,3,0,击败21波敌人。,7,21,1,UI_MainPanel,item_10000001_235,0,0,null
|
||||||
|
30004,3,0,击败28波敌人。,7,28,1,UI_MainPanel,item_10000001_236,0,0,null
|
||||||
|
30005,3,0,击败35波敌人。,7,35,1,UI_MainPanel,item_10000001_237,0,0,null
|
||||||
|
30006,3,0,击败42波敌人。,7,42,1,UI_MainPanel,item_10000001_238,0,0,null
|
||||||
|
30007,3,0,击败49波敌人。,7,49,1,UI_MainPanel,item_10000001_239,0,0,null
|
||||||
|
30008,3,0,击败56波敌人。,7,56,1,UI_MainPanel,item_10000001_240,0,0,null
|
||||||
|
30009,3,0,击败63波敌人。,7,63,1,UI_MainPanel,item_10000001_241,0,0,null
|
||||||
|
30010,3,0,击败70波敌人。,7,70,1,UI_MainPanel,item_10000001_242,0,0,null
|
||||||
|
30011,3,0,招募5次。,1,5,2,UI_DrawCardPanel,item_10000001_243,0,0,null
|
||||||
|
30012,3,0,招募5次。,1,5,2,UI_DrawCardPanel,item_10000001_244,0,0,null
|
||||||
|
30013,3,0,招募5次。,1,5,2,UI_DrawCardPanel,item_10000001_245,0,0,null
|
||||||
|
30014,3,0,招募10次。,1,10,2,UI_DrawCardPanel,item_10000001_246,0,0,null
|
||||||
|
30015,3,0,招募10次。,1,10,2,UI_DrawCardPanel,item_10000001_247,0,0,null
|
||||||
|
30016,3,0,招募15次。,1,15,2,UI_DrawCardPanel,item_10000001_248,0,0,null
|
||||||
|
30017,3,0,招募15次。,1,15,2,UI_DrawCardPanel,item_10000001_249,0,0,null
|
||||||
|
30018,3,0,招募20次。,1,20,2,UI_DrawCardPanel,item_10000001_250,0,0,null
|
||||||
|
30019,3,0,招募20次。,1,20,2,UI_DrawCardPanel,item_10000001_251,0,0,null
|
||||||
|
30020,3,0,招募25次。,1,25,2,UI_DrawCardPanel,item_10000001_252,0,0,null
|
||||||
|
30021,3,0,消耗300勾玉。,10,300,3,UI_ShopPanel,item_10000001_253,0,0,null
|
||||||
|
30022,3,0,消耗500勾玉。,10,500,3,UI_ShopPanel,item_10000001_254,0,0,null
|
||||||
|
30023,3,0,消耗300勾玉。,10,300,3,UI_ShopPanel,item_10000001_255,0,0,null
|
||||||
|
30024,3,0,消耗500勾玉。,10,500,3,UI_ShopPanel,item_10000001_256,0,0,null
|
||||||
|
30025,3,0,消耗300勾玉。,10,300,3,UI_ShopPanel,item_10000001_257,0,0,null
|
||||||
|
30026,3,0,消耗500勾玉。,10,500,3,UI_ShopPanel,item_10000001_258,0,0,null
|
||||||
|
30027,3,0,消耗300勾玉。,10,300,3,UI_ShopPanel,item_10000001_259,0,0,null
|
||||||
|
30028,3,0,消耗500勾玉。,10,500,3,UI_ShopPanel,item_10000001_260,0,0,null
|
||||||
|
30029,3,0,消耗300勾玉。,10,300,3,UI_ShopPanel,item_10000001_261,0,0,null
|
||||||
|
30030,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_262,0,0,null
|
||||||
|
30031,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_263,0,0,null
|
||||||
|
30032,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_264,0,0,null
|
||||||
|
30033,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_265,0,0,null
|
||||||
|
30034,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_266,0,0,null
|
||||||
|
30035,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_267,0,0,null
|
||||||
|
30036,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_268,0,0,null
|
||||||
|
30037,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_269,0,0,null
|
||||||
|
30038,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_270,0,0,null
|
||||||
|
30039,3,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000001_271,0,0,null
|
||||||
|
30040,3,0,消耗10万金币。,9,100000,3,UI_ShopPanel,item_10000001_272,0,0,null
|
||||||
|
30041,3,0,消耗10万金币。,9,100000,3,UI_ShopPanel,item_10000001_273,0,0,null
|
||||||
|
30042,3,0,消耗20万金币。,9,200000,3,UI_ShopPanel,item_10000001_274,0,0,null
|
||||||
|
30043,3,0,消耗20万金币。,9,200000,3,UI_ShopPanel,item_10000001_275,0,0,null
|
||||||
|
30044,3,0,消耗30万金币。,9,300000,3,UI_ShopPanel,item_10000001_276,0,0,null
|
||||||
|
30045,3,0,消耗30万金币。,9,300000,3,UI_ShopPanel,item_10000001_277,0,0,null
|
||||||
|
30046,3,0,消耗50万金币。,9,500000,3,UI_ShopPanel,item_10000001_278,0,0,null
|
||||||
|
30047,3,0,消耗50万金币。,9,500000,3,UI_ShopPanel,item_10000001_279,0,0,null
|
||||||
|
30048,3,0,消耗60万金币。,9,600000,3,UI_ShopPanel,item_10000001_280,0,0,null
|
||||||
|
30049,3,0,消耗60万金币。,9,600000,3,UI_ShopPanel,item_10000001_281,0,0,null
|
||||||
|
30050,3,0,消耗100万金币。,9,1000000,3,UI_ShopPanel,item_10000001_282,0,0,null
|
||||||
|
30051,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_283,0,0,null
|
||||||
|
30052,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_284,0,0,null
|
||||||
|
30053,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_285,0,0,null
|
||||||
|
30054,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_286,0,0,null
|
||||||
|
30055,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_287,0,0,null
|
||||||
|
30056,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_288,0,0,null
|
||||||
|
30057,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_289,0,0,null
|
||||||
|
30058,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_290,0,0,null
|
||||||
|
30059,3,0,挑战5次副本。,13,5,4,UI_FBPanel,item_10000001_291,0,0,null
|
||||||
|
30060,3,0,挑战50次副本。,13,50,4,UI_FBPanel,item_10000001_292,0,0,null
|
||||||
|
40001,4,0,击败14波敌人。,7,14,1,UI_MainPanel,item_10000002_150,0,0,null
|
||||||
|
40002,4,0,击败21波敌人。,7,21,1,UI_MainPanel,item_10000002_170,0,0,null
|
||||||
|
40003,4,0,击败28波敌人。,7,28,1,UI_MainPanel,item_10000002_200,0,0,null
|
||||||
|
40004,4,0,击败35波敌人。,7,35,1,UI_MainPanel,item_10000002_250,0,0,null
|
||||||
|
40005,4,0,招募5次。,1,5,2,UI_DrawCardPanel,item_10000002_100,0,0,null
|
||||||
|
40006,4,0,招募10次。,1,10,2,UI_DrawCardPanel,item_10000002_150,0,0,null
|
||||||
|
40007,4,0,招募20次。,1,20,2,UI_DrawCardPanel,item_10000002_170,0,0,null
|
||||||
|
40008,4,0,招募30次。,1,30,2,UI_DrawCardPanel,item_10000002_200,0,0,null
|
||||||
|
40009,4,0,招募50次。,1,50,2,UI_DrawCardPanel,item_10000002_250,0,0,null
|
||||||
|
40010,4,0,挑战2次副本。,13,2,4,UI_FBPanel,item_10000002_100,0,0,null
|
||||||
|
40011,4,0,挑战2次竞技场。,14,2,10,UI_ArePanel,item_10000002_150,0,0,null
|
||||||
|
40012,4,0,挑战3次副本。,13,3,4,UI_FBPanel,item_10000002_170,0,0,null
|
||||||
|
40013,4,0,挑战3次竞技场。,14,3,10,UI_ArePanel,item_10000002_200,0,0,null
|
||||||
|
40014,4,0,挑战5次竞技场。,14,5,10,UI_ArePanel,item_10000002_250,0,0,null
|
||||||
|
40015,4,0,击败7波敌人。,7,7,1,UI_MainPanel,item_10000002_100,0,0,null
|
||||||
|
50001,5,0,消耗25万金币。,9,250000,3,UI_ShopPanel,item_10000001_15000,20,0,null
|
||||||
|
50002,5,0,招募5次。,1,5,2,UI_DrawCardPanel,item_10000001_25000,20,0,null
|
||||||
|
50003,5,0,英雄升级15次。,2,15,8,UI_HeroPanel,item_30000003_3,20,0,null
|
||||||
|
50004,5,0,领取10次游历奖励。,16,10,5,UI_IdlePanel,item_30000005_3,20,0,null
|
||||||
|
50005,5,0,挑战10次竞技场。,14,10,10,UI_ArePanel,item_20000022_7500,20,0,null
|
||||||
|
50006,5,0,挑战15次副本。,13,15,4,UI_FBPanel,item_20000001_3,20,0,null
|
||||||
|
50007,5,0,装备升级5次。,5,5,7,UI_EquipPanel,item_20000002_3,20,0,null
|
||||||
|
50008,5,0,通过5关主线关卡。,6,5,1,UI_MainPanel,item_20000007_80,20,0,null
|
||||||
|
50009,5,0,击败35波敌人。,7,35,1,UI_MainPanel,item_20000008_80,20,0,null
|
||||||
|
50010,5,0,商店购买15次。,8,15,3,UI_ShopPanel,item_20000009_80,20,0,null
|
||||||
|
50011,5,0,主角升级3次。,4,3,8,UI_HeroPanel,item_20000010_80,20,0,null
|
||||||
|
50012,5,0,消耗500勾玉。,10,500,3,UI_ShopPanel,item_20000011_80,20,0,null
|
||||||
|
50013,5,0,登录5天。,11,5,1,UI_MainPanel,item_30000005_3,20,0,null
|
||||||
|
90001,9,0,累计获得3位英雄。,15,3,2,UI_DrawCardPanel,item_10000002_100,25,0,null
|
||||||
|
90002,9,0,累计获得6位英雄。,15,6,2,UI_DrawCardPanel,item_10000002_100,25,0,null
|
||||||
|
90003,9,0,累计获得9位英雄。,15,9,2,UI_DrawCardPanel,item_10000002_100,25,0,null
|
||||||
|
90004,9,0,累计获得12位英雄。,15,12,2,UI_DrawCardPanel,item_10000002_150,25,0,null
|
||||||
|
90005,9,0,累计获得15位英雄。,15,15,2,UI_DrawCardPanel,item_10000002_150,25,0,null
|
||||||
|
90006,9,0,累计获得24位英雄。,15,24,2,UI_DrawCardPanel,item_10000002_200,25,0,null
|
||||||
|
90007,9,0,累计招募50次。,1,50,2,UI_DrawCardPanel,item_10000001_10,25,0,null
|
||||||
|
90008,9,0,累计招募150次。,1,150,2,UI_DrawCardPanel,item_10000001_30,25,0,null
|
||||||
|
90009,9,0,累计招募250次。,1,250,2,UI_DrawCardPanel,item_10000001_50,25,0,null
|
||||||
|
90010,9,0,累计招募350次。,1,350,2,UI_DrawCardPanel,item_10000001_70,25,0,null
|
||||||
|
90011,9,0,累计招募450次。,1,450,2,UI_DrawCardPanel,item_10000001_90,25,0,null
|
||||||
|
90012,9,0,累计招募550次。,1,550,2,UI_DrawCardPanel,item_10000001_110,25,0,null
|
||||||
|
90013,9,0,累计招募650次。,1,650,2,UI_DrawCardPanel,item_10000001_130,25,0,null
|
||||||
|
90014,9,0,累计招募750次。,1,750,2,UI_DrawCardPanel,item_10000001_150,25,0,null
|
||||||
|
90015,9,0,累计招募850次。,1,850,2,UI_DrawCardPanel,item_10000001_170,25,0,null
|
||||||
|
90016,9,0,累计招募950次。,1,950,2,UI_DrawCardPanel,item_10000001_190,25,0,null
|
||||||
|
90017,9,0,装备累计升级6000次。,5,6000,7,UI_EquipPanel,item_20000017_1000,25,0,null
|
||||||
|
90018,9,0,装备累计升级1万次。,5,10000,7,UI_EquipPanel,item_20000017_2000,25,0,null
|
||||||
|
90019,9,0,装备累计升级2万次。,5,20000,7,UI_EquipPanel,item_20000017_3000,25,0,null
|
||||||
|
90020,9,0,装备累计升级4万次。,5,40000,7,UI_EquipPanel,item_20000017_4000,25,0,null
|
||||||
|
90021,9,0,装备累计升级6万次。,5,60000,7,UI_EquipPanel,item_20000017_5000,25,0,null
|
||||||
|
90022,9,0,装备累计升级8万次。,5,80000,7,UI_EquipPanel,item_20000017_6000,25,0,null
|
||||||
|
90023,9,0,装备累计升级10万次。,5,100000,7,UI_EquipPanel,item_20000017_7000,25,0,null
|
||||||
|
90024,9,0,装备累计升级12万次。,5,120000,7,UI_EquipPanel,item_20000017_8000,25,0,null
|
||||||
|
90025,9,0,装备累计升级14万次。,5,140000,7,UI_EquipPanel,item_20000017_9000,25,0,null
|
||||||
|
90026,9,0,装备累计升级16万次。,5,160000,7,UI_EquipPanel,item_20000017_10000,25,0,null
|
||||||
|
90027,9,0,装备累计升级18万次。,5,180000,7,UI_EquipPanel,item_20000017_11000,25,0,null
|
||||||
|
90028,9,0,装备累计升级20万次。,5,200000,7,UI_EquipPanel,item_20000017_12000item_20000018_6000,25,0,null
|
||||||
|
90029,9,0,装备累计升级22万次。,5,220000,7,UI_EquipPanel,item_20000017_13000item_20000018_6500,25,0,null
|
||||||
|
90030,9,0,装备累计升级24万次。,5,240000,7,UI_EquipPanel,item_20000017_14000item_20000018_7000,25,0,null
|
||||||
|
90031,9,0,累计通关5万波敌人。,7,50000,1,UI_MainPanel,item_10000001_12500,25,0,null
|
||||||
|
90032,9,0,累计通关10万波敌人。,7,100000,1,UI_MainPanel,item_10000001_25000,25,0,null
|
||||||
|
90033,9,0,累计通关50万波敌人。,7,500000,1,UI_MainPanel,item_10000001_125000,25,0,null
|
||||||
|
90034,9,0,累计通关100万波敌人。,7,1000000,1,UI_MainPanel,item_10000001_250000,25,0,null
|
||||||
|
90035,9,0,累计通关200万波敌人。,7,2000000,1,UI_MainPanel,item_10000001_500000,25,0,null
|
||||||
|
90036,9,0,累计通关300万波敌人。,7,3000000,1,UI_MainPanel,item_10000001_750000,25,0,null
|
||||||
|
90037,9,0,累计通关400万波敌人。,7,4000000,1,UI_MainPanel,item_10000001_1000000,25,0,null
|
||||||
|
90038,9,0,累计通关500万波敌人。,7,5000000,1,UI_MainPanel,item_10000001_1250000,25,0,null
|
||||||
|
90039,9,0,累计通关600万波敌人。,7,6000000,1,UI_MainPanel,item_10000001_1500000,25,0,null
|
||||||
|
90040,9,0,累计通关700万波敌人。,7,7000000,1,UI_MainPanel,item_10000001_1750000,25,0,null
|
||||||
|
90041,9,0,累计通关800万波敌人。,7,8000000,1,UI_MainPanel,item_10000001_2000000,25,0,null
|
||||||
|
90042,9,0,累计通关900万波敌人。,7,9000000,1,UI_MainPanel,item_10000001_2250000,25,0,null
|
||||||
|
90043,9,0,商店累计消费50次。,8,50,3,UI_ShopPanel,item_30000001_5,25,0,null
|
||||||
|
90044,9,0,商店累计消费100次。,8,100,3,UI_ShopPanel,item_30000001_10,25,0,null
|
||||||
|
90045,9,0,商店累计消费150次。,8,150,3,UI_ShopPanel,item_30000001_15,25,0,null
|
||||||
|
90046,9,0,商店累计消费200次。,8,200,3,UI_ShopPanel,item_30000001_20,25,0,null
|
||||||
|
90047,9,0,商店累计消费250次。,8,250,3,UI_ShopPanel,item_30000001_25,25,0,null
|
||||||
|
90048,9,0,商店累计消费300次。,8,300,3,UI_ShopPanel,item_30000001_30,25,0,null
|
||||||
|
90049,9,0,商店累计消费350次。,8,350,3,UI_ShopPanel,item_30000001_35,25,0,null
|
||||||
|
90050,9,0,商店累计消费400次。,8,400,3,UI_ShopPanel,item_30000001_40,25,0,null
|
||||||
|
90051,9,0,商店累计消费450次。,8,450,3,UI_ShopPanel,item_30000001_45,25,0,null
|
||||||
|
90052,9,0,商店累计消费500次。,8,500,3,UI_ShopPanel,item_30000001_50,25,0,null
|
||||||
|
90053,9,0,商店累计消费550次。,8,550,3,UI_ShopPanel,item_30000001_55,25,0,null
|
||||||
|
90054,9,0,商店累计消费10次。,8,10,3,UI_ShopPanel,item_10000002_100,25,0,null
|
||||||
|
90055,9,0,商店累计消费20次。,8,20,3,UI_ShopPanel,item_10000002_100,25,0,null
|
||||||
|
90056,9,0,商店累计消费30次。,8,30,3,UI_ShopPanel,item_10000002_100,25,0,null
|
||||||
|
90057,9,0,商店累计消费40次。,8,40,3,UI_ShopPanel,item_10000002_150,25,0,null
|
||||||
|
90058,9,0,商店累计消费50次。,8,50,3,UI_ShopPanel,item_10000002_150,25,0,null
|
||||||
|
90059,9,0,商店累计消费60次。,8,60,3,UI_ShopPanel,item_10000002_200,25,0,null
|
||||||
|
90060,9,0,商店累计消费10次。,8,10,3,UI_ShopPanel,item_10000002_100,25,0,null
|
||||||
|
90061,9,0,商店累计消费30次。,8,30,3,UI_ShopPanel,item_10000002_100,25,0,null
|
||||||
|
90062,9,0,商店累计消费50次。,8,50,3,UI_ShopPanel,item_10000002_100,25,0,null
|
||||||
|
90063,9,0,商店累计消费70次。,8,70,3,UI_ShopPanel,item_10000002_150,25,0,null
|
||||||
|
90064,9,0,商店累计消费90次。,8,90,3,UI_ShopPanel,item_10000002_150,25,0,null
|
||||||
|
90065,9,0,商店累计消费110次。,8,110,3,UI_ShopPanel,item_10000002_200,25,0,null
|
||||||
|
401097
data_index.json
Normal file
401097
data_index.json
Normal file
File diff suppressed because it is too large
Load Diff
304
data_indexer.py
Normal file
304
data_indexer.py
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import os
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class DataIndexer:
|
||||||
|
def __init__(self, csv_dir='csv_output'):
|
||||||
|
self.csv_dir = csv_dir
|
||||||
|
self.index = {}
|
||||||
|
self.metadata = {}
|
||||||
|
|
||||||
|
def build_index(self, table_name):
|
||||||
|
csv_path = os.path.join(self.csv_dir, f"{table_name}.csv")
|
||||||
|
|
||||||
|
if not os.path.exists(csv_path):
|
||||||
|
print(f"错误: 文件不存在 - {csv_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
index = {
|
||||||
|
'table_name': table_name,
|
||||||
|
'created_at': datetime.now().isoformat(),
|
||||||
|
'record_count': 0,
|
||||||
|
'indexes': {
|
||||||
|
'by_id': {},
|
||||||
|
'by_category': defaultdict(list),
|
||||||
|
'by_type': defaultdict(list)
|
||||||
|
},
|
||||||
|
'stats': {
|
||||||
|
'category_distribution': defaultdict(int),
|
||||||
|
'type_distribution': defaultdict(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(csv_path, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
if len(lines) < 3:
|
||||||
|
return False
|
||||||
|
|
||||||
|
fieldnames = lines[2].strip().split(',')
|
||||||
|
|
||||||
|
data_lines = lines[3:]
|
||||||
|
reader = csv.DictReader(data_lines, fieldnames=fieldnames)
|
||||||
|
|
||||||
|
for row_num, row in enumerate(reader, start=4):
|
||||||
|
row_id = row.get('id')
|
||||||
|
if not row_id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
row_id_int = int(row_id)
|
||||||
|
category = int(row.get('category', 0))
|
||||||
|
type_val = int(row.get('type', 0))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
record = {
|
||||||
|
'row_num': row_num,
|
||||||
|
'data': row
|
||||||
|
}
|
||||||
|
|
||||||
|
index['indexes']['by_id'][row_id_int] = record
|
||||||
|
index['indexes']['by_category'][category].append(row_id_int)
|
||||||
|
index['indexes']['by_type'][type_val].append(row_id_int)
|
||||||
|
|
||||||
|
index['stats']['category_distribution'][category] += 1
|
||||||
|
index['stats']['type_distribution'][type_val] += 1
|
||||||
|
index['record_count'] += 1
|
||||||
|
|
||||||
|
self.index[table_name] = index
|
||||||
|
self.metadata[table_name] = {
|
||||||
|
'last_indexed': datetime.now().isoformat(),
|
||||||
|
'record_count': index['record_count']
|
||||||
|
}
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"索引构建失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def save_index(self, output_file='data_index.json'):
|
||||||
|
data = {
|
||||||
|
'metadata': self.metadata,
|
||||||
|
'index': self.index,
|
||||||
|
'generated_at': datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
print(f"索引已保存至: {output_file}")
|
||||||
|
|
||||||
|
def load_index(self, index_file='data_index.json'):
|
||||||
|
if os.path.exists(index_file):
|
||||||
|
with open(index_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
self.metadata = data.get('metadata', {})
|
||||||
|
self.index = data.get('index', {})
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def query_by_id(self, table_name, id_val):
|
||||||
|
if table_name not in self.index:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.index[table_name]['indexes']['by_id'].get(id_val)
|
||||||
|
|
||||||
|
def query_by_category(self, table_name, category):
|
||||||
|
if table_name not in self.index:
|
||||||
|
return []
|
||||||
|
|
||||||
|
ids = self.index[table_name]['indexes']['by_category'].get(category, [])
|
||||||
|
results = []
|
||||||
|
for row_id in ids:
|
||||||
|
record = self.index[table_name]['indexes']['by_id'].get(row_id)
|
||||||
|
if record:
|
||||||
|
results.append(record)
|
||||||
|
return results
|
||||||
|
|
||||||
|
def query_by_type(self, table_name, type_val):
|
||||||
|
if table_name not in self.index:
|
||||||
|
return []
|
||||||
|
|
||||||
|
ids = self.index[table_name]['indexes']['by_type'].get(type_val, [])
|
||||||
|
results = []
|
||||||
|
for row_id in ids:
|
||||||
|
record = self.index[table_name]['indexes']['by_id'].get(row_id)
|
||||||
|
if record:
|
||||||
|
results.append(record)
|
||||||
|
return results
|
||||||
|
|
||||||
|
def query_by_category_and_type(self, table_name, category, type_val):
|
||||||
|
if table_name not in self.index:
|
||||||
|
return []
|
||||||
|
|
||||||
|
category_ids = set(self.index[table_name]['indexes']['by_category'].get(category, []))
|
||||||
|
type_ids = set(self.index[table_name]['indexes']['by_type'].get(type_val, []))
|
||||||
|
|
||||||
|
common_ids = category_ids & type_ids
|
||||||
|
results = []
|
||||||
|
for row_id in common_ids:
|
||||||
|
record = self.index[table_name]['indexes']['by_id'].get(row_id)
|
||||||
|
if record:
|
||||||
|
results.append(record)
|
||||||
|
return results
|
||||||
|
|
||||||
|
def get_stats(self, table_name):
|
||||||
|
if table_name not in self.index:
|
||||||
|
return None
|
||||||
|
return self.index[table_name]['stats']
|
||||||
|
|
||||||
|
def list_tables(self):
|
||||||
|
tables = []
|
||||||
|
for filename in os.listdir(self.csv_dir):
|
||||||
|
if filename.endswith('.csv'):
|
||||||
|
table_name = filename[:-4]
|
||||||
|
tables.append(table_name)
|
||||||
|
return sorted(tables)
|
||||||
|
|
||||||
|
def build_all_indexes(self):
|
||||||
|
tables = self.list_tables()
|
||||||
|
print(f"发现 {len(tables)} 个CSV文件")
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
for table_name in tables:
|
||||||
|
print(f"正在索引: {table_name}...", end=' ')
|
||||||
|
if self.build_index(table_name):
|
||||||
|
print(f"完成 ({self.index[table_name]['record_count']} 条记录)")
|
||||||
|
success_count += 1
|
||||||
|
else:
|
||||||
|
print("失败")
|
||||||
|
failed_count += 1
|
||||||
|
|
||||||
|
print(f"\n索引构建完成: {success_count} 成功, {failed_count} 失败")
|
||||||
|
|
||||||
|
def generate_index_report(self):
|
||||||
|
report = []
|
||||||
|
report.append("=" * 80)
|
||||||
|
report.append("数据索引报告")
|
||||||
|
report.append("=" * 80)
|
||||||
|
report.append(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
|
report.append(f"已索引表数量: {len(self.index)}")
|
||||||
|
report.append("")
|
||||||
|
|
||||||
|
for table_name in sorted(self.index.keys()):
|
||||||
|
idx = self.index[table_name]
|
||||||
|
report.append(f"【{table_name}】")
|
||||||
|
report.append("-" * 60)
|
||||||
|
report.append(f" 记录数: {idx['record_count']}")
|
||||||
|
report.append(f" 索引时间: {idx['created_at']}")
|
||||||
|
report.append("")
|
||||||
|
|
||||||
|
report.append(" 分类分布:")
|
||||||
|
for category, count in sorted(idx['stats']['category_distribution'].items()):
|
||||||
|
report.append(f" {category}: {count} 条")
|
||||||
|
|
||||||
|
report.append("")
|
||||||
|
report.append(" 类型分布(前10):")
|
||||||
|
sorted_types = sorted(idx['stats']['type_distribution'].items(), key=lambda x: x[1], reverse=True)[:10]
|
||||||
|
for type_val, count in sorted_types:
|
||||||
|
report.append(f" {type_val}: {count} 条")
|
||||||
|
if len(idx['stats']['type_distribution']) > 10:
|
||||||
|
report.append(f" ... 还有 {len(idx['stats']['type_distribution']) - 10} 种类型")
|
||||||
|
|
||||||
|
report.append("")
|
||||||
|
|
||||||
|
report.append("=" * 80)
|
||||||
|
|
||||||
|
return "\n".join(report)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='数据索引系统')
|
||||||
|
parser.add_argument('-a', '--action', required=True, choices=['build', 'query', 'report'], help='操作类型')
|
||||||
|
parser.add_argument('-t', '--table', help='表名')
|
||||||
|
parser.add_argument('-id', '--id', type=int, help='按ID查询')
|
||||||
|
parser.add_argument('-c', '--category', type=int, help='按分类查询')
|
||||||
|
parser.add_argument('-type', '--type', type=int, help='按类型查询')
|
||||||
|
parser.add_argument('-o', '--output', help='输出文件')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
indexer = DataIndexer()
|
||||||
|
|
||||||
|
if args.action == 'build':
|
||||||
|
indexer.build_all_indexes()
|
||||||
|
indexer.save_index()
|
||||||
|
|
||||||
|
report = indexer.generate_index_report()
|
||||||
|
print("\n" + report)
|
||||||
|
|
||||||
|
if args.output:
|
||||||
|
with open(args.output, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(report)
|
||||||
|
print(f"\n报告已保存至: {args.output}")
|
||||||
|
|
||||||
|
elif args.action == 'query':
|
||||||
|
if not args.table:
|
||||||
|
print("错误: 查询需要指定表名")
|
||||||
|
return
|
||||||
|
|
||||||
|
indexer.load_index()
|
||||||
|
|
||||||
|
if args.id is not None:
|
||||||
|
result = indexer.query_by_id(args.table, args.id)
|
||||||
|
if result:
|
||||||
|
print(f"查询结果 (id={args.id}):")
|
||||||
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||||
|
else:
|
||||||
|
print(f"未找到 id={args.id} 的记录")
|
||||||
|
|
||||||
|
elif args.category is not None and args.type is not None:
|
||||||
|
results = indexer.query_by_category_and_type(args.table, args.category, args.type)
|
||||||
|
print(f"查询结果 (category={args.category}, type={args.type}):")
|
||||||
|
print(f"共找到 {len(results)} 条记录")
|
||||||
|
for r in results[:5]:
|
||||||
|
print(f" id={r['data']['id']}: {r['data']['desc']}")
|
||||||
|
if len(results) > 5:
|
||||||
|
print(f" ... 还有 {len(results) - 5} 条")
|
||||||
|
|
||||||
|
elif args.category is not None:
|
||||||
|
results = indexer.query_by_category(args.table, args.category)
|
||||||
|
print(f"查询结果 (category={args.category}):")
|
||||||
|
print(f"共找到 {len(results)} 条记录")
|
||||||
|
for r in results[:5]:
|
||||||
|
print(f" id={r['data']['id']}: {r['data']['desc']}")
|
||||||
|
if len(results) > 5:
|
||||||
|
print(f" ... 还有 {len(results) - 5} 条")
|
||||||
|
|
||||||
|
elif args.type is not None:
|
||||||
|
results = indexer.query_by_type(args.table, args.type)
|
||||||
|
print(f"查询结果 (type={args.type}):")
|
||||||
|
print(f"共找到 {len(results)} 条记录")
|
||||||
|
for r in results[:5]:
|
||||||
|
print(f" id={r['data']['id']}: {r['data']['desc']}")
|
||||||
|
if len(results) > 5:
|
||||||
|
print(f" ... 还有 {len(results) - 5} 条")
|
||||||
|
|
||||||
|
else:
|
||||||
|
stats = indexer.get_stats(args.table)
|
||||||
|
if stats:
|
||||||
|
print(f"表 {args.table} 的统计信息:")
|
||||||
|
print(json.dumps(stats, ensure_ascii=False, indent=2))
|
||||||
|
else:
|
||||||
|
print(f"未找到表 {args.table} 的索引")
|
||||||
|
|
||||||
|
elif args.action == 'report':
|
||||||
|
indexer.load_index()
|
||||||
|
report = indexer.generate_index_report()
|
||||||
|
print(report)
|
||||||
|
|
||||||
|
if args.output:
|
||||||
|
with open(args.output, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(report)
|
||||||
|
print(f"\n报告已保存至: {args.output}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
485
data_validator.py
Normal file
485
data_validator.py
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
import os
|
||||||
|
import csv
|
||||||
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
def format_number(num):
|
||||||
|
if num >= 100000000:
|
||||||
|
return f"{num // 100000000}亿"
|
||||||
|
elif num >= 10000:
|
||||||
|
return f"{num // 10000}万"
|
||||||
|
else:
|
||||||
|
return str(num)
|
||||||
|
|
||||||
|
TYPE_DESCRIPTIONS = {
|
||||||
|
1: "招募",
|
||||||
|
2: "英雄升级",
|
||||||
|
3: "英雄升阶",
|
||||||
|
4: "主角升级",
|
||||||
|
5: "装备升级",
|
||||||
|
6: "通过",
|
||||||
|
7: "击败",
|
||||||
|
8: "商店购买",
|
||||||
|
9: "消耗",
|
||||||
|
10: "消耗",
|
||||||
|
11: "登录",
|
||||||
|
12: "开",
|
||||||
|
13: "挑战",
|
||||||
|
14: "挑战",
|
||||||
|
15: "获得",
|
||||||
|
16: "领取",
|
||||||
|
17: "世界频道发言"
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPE_UNITS = {
|
||||||
|
1: "次",
|
||||||
|
2: "次",
|
||||||
|
3: "次",
|
||||||
|
4: "次",
|
||||||
|
5: "次",
|
||||||
|
6: "关主线关卡",
|
||||||
|
7: "波敌人",
|
||||||
|
8: "次",
|
||||||
|
9: "金币",
|
||||||
|
10: "勾玉",
|
||||||
|
11: "天",
|
||||||
|
12: "次宝箱",
|
||||||
|
13: "次副本",
|
||||||
|
14: "次竞技场",
|
||||||
|
15: "个英雄",
|
||||||
|
16: "次游历奖励",
|
||||||
|
17: "次"
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPE_DESCRIPTIONS_ACHIEVEMENT = {
|
||||||
|
1: "累计招募",
|
||||||
|
2: "英雄累计升级",
|
||||||
|
3: "英雄累计升阶",
|
||||||
|
4: "主角累计升级",
|
||||||
|
5: "装备累计升级",
|
||||||
|
6: "累计通关",
|
||||||
|
7: "累计通关",
|
||||||
|
8: "商店累计消费",
|
||||||
|
9: "累计消耗",
|
||||||
|
10: "累计消耗",
|
||||||
|
11: "累计登录",
|
||||||
|
12: "无",
|
||||||
|
13: "累计挑战副本",
|
||||||
|
14: "累计挑战竞技场",
|
||||||
|
15: "累计获得",
|
||||||
|
16: "累计领取",
|
||||||
|
17: "累计消耗",
|
||||||
|
18: "累计点赞好友",
|
||||||
|
19: "累计英雄委托",
|
||||||
|
20: "累计击败",
|
||||||
|
21: "战力达到",
|
||||||
|
22: "主角到达",
|
||||||
|
23: "累计委托"
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPE_UNITS_ACHIEVEMENT = {
|
||||||
|
1: "次",
|
||||||
|
2: "次",
|
||||||
|
3: "次",
|
||||||
|
4: "次",
|
||||||
|
5: "次",
|
||||||
|
6: "关主线关卡",
|
||||||
|
7: "波敌人",
|
||||||
|
8: "次",
|
||||||
|
9: "金币",
|
||||||
|
10: "钻石",
|
||||||
|
11: "天",
|
||||||
|
12: "",
|
||||||
|
13: "次",
|
||||||
|
14: "次",
|
||||||
|
15: "位英雄",
|
||||||
|
16: "次挂机奖励",
|
||||||
|
17: "秒",
|
||||||
|
18: "次",
|
||||||
|
19: "次",
|
||||||
|
20: "个敌人",
|
||||||
|
21: "万",
|
||||||
|
22: "阶",
|
||||||
|
23: "次"
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPE_TO_OPENUI = {
|
||||||
|
1: 2,
|
||||||
|
2: 8,
|
||||||
|
3: 8,
|
||||||
|
4: 8,
|
||||||
|
5: 7,
|
||||||
|
6: 1,
|
||||||
|
7: 1,
|
||||||
|
8: 3,
|
||||||
|
9: 3,
|
||||||
|
10: 3,
|
||||||
|
11: 1,
|
||||||
|
12: 1,
|
||||||
|
13: 4,
|
||||||
|
14: 10,
|
||||||
|
15: 2,
|
||||||
|
16: 5,
|
||||||
|
17: 6
|
||||||
|
}
|
||||||
|
|
||||||
|
OPENUI_TO_DESC = {
|
||||||
|
1: "UI_MainPanel",
|
||||||
|
2: "UI_DrawCardPanel",
|
||||||
|
3: "UI_ShopPanel",
|
||||||
|
4: "UI_FBPanel",
|
||||||
|
5: "UI_IdlePanel",
|
||||||
|
6: "UI_ChatPanel",
|
||||||
|
7: "UI_EquipPanel",
|
||||||
|
8: "UI_HeroPanel",
|
||||||
|
9: "UI_FriendPanel",
|
||||||
|
10: "UI_ArePanel"
|
||||||
|
}
|
||||||
|
|
||||||
|
CATEGORY_SCORE_MAP = {
|
||||||
|
2: 20,
|
||||||
|
5: 20,
|
||||||
|
6: 20,
|
||||||
|
10: 20,
|
||||||
|
9: 25
|
||||||
|
}
|
||||||
|
|
||||||
|
CATEGORY_DESCRIPTIONS = {
|
||||||
|
1: "主线任务",
|
||||||
|
2: "日常任务",
|
||||||
|
3: "通行证任务",
|
||||||
|
4: "修炼任务",
|
||||||
|
5: "周常任务",
|
||||||
|
6: "公会任务",
|
||||||
|
7: "委托任务",
|
||||||
|
8: "称号任务",
|
||||||
|
9: "成就任务",
|
||||||
|
10: "七日任务"
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValidationError:
|
||||||
|
def __init__(self, row_num, field, error_type, message):
|
||||||
|
self.row_num = row_num
|
||||||
|
self.field = field
|
||||||
|
self.error_type = error_type
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"行{self.row_num} [{self.field}] {self.error_type}: {self.message}"
|
||||||
|
|
||||||
|
class QuestValidator:
|
||||||
|
def __init__(self):
|
||||||
|
self.errors = []
|
||||||
|
self.all_ids = set()
|
||||||
|
self.id_to_row = {}
|
||||||
|
self.category_ids = defaultdict(list)
|
||||||
|
|
||||||
|
def _add_error(self, row_num, field, error_type, message):
|
||||||
|
self.errors.append(ValidationError(row_num, field, error_type, message))
|
||||||
|
|
||||||
|
def _parse_int(self, value, default=None):
|
||||||
|
try:
|
||||||
|
return int(value)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return default
|
||||||
|
|
||||||
|
def _parse_str(self, value):
|
||||||
|
if value is None:
|
||||||
|
return ""
|
||||||
|
return str(value).strip()
|
||||||
|
|
||||||
|
def validate_id(self, row_num, id_val, category):
|
||||||
|
id_str = str(id_val)
|
||||||
|
|
||||||
|
if len(id_str) != 5:
|
||||||
|
self._add_error(row_num, "id", "格式错误", f"id必须为5位数字,当前值: {id_val}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not id_str.isdigit():
|
||||||
|
self._add_error(row_num, "id", "格式错误", f"id必须为纯数字,当前值: {id_val}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
expected_category = int(id_str[0])
|
||||||
|
if expected_category != category:
|
||||||
|
self._add_error(row_num, "id", "逻辑错误", f"id第一位({expected_category})与category({category})不匹配")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_category(self, row_num, category):
|
||||||
|
if category not in CATEGORY_DESCRIPTIONS:
|
||||||
|
self._add_error(row_num, "category", "枚举错误", f"category值{category}不在允许范围1-10内")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_next(self, row_num, next_val, category):
|
||||||
|
if next_val < 0:
|
||||||
|
self._add_error(row_num, "next", "范围错误", f"next不能为负数,当前值: {next_val}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if category != 1:
|
||||||
|
if next_val != 0:
|
||||||
|
self._add_error(row_num, "next", "规则错误", f"category≠1时next必须为0,当前值: {next_val}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if next_val != 0 and next_val not in self.all_ids:
|
||||||
|
self._add_error(row_num, "next", "引用错误", f"next={next_val}引用的id不存在")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_desc(self, row_num, desc, type_val, target, category):
|
||||||
|
desc = self._parse_str(desc)
|
||||||
|
|
||||||
|
if not desc:
|
||||||
|
self._add_error(row_num, "desc", "空值错误", "desc字段不能为空")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not desc.endswith("。"):
|
||||||
|
self._add_error(row_num, "desc", "格式错误", f"desc必须以中文句号'。'结尾,当前值: '{desc}'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if category == 9:
|
||||||
|
if type_val not in TYPE_DESCRIPTIONS_ACHIEVEMENT:
|
||||||
|
return True
|
||||||
|
formatted_target = format_number(target)
|
||||||
|
expected_desc = f"{TYPE_DESCRIPTIONS_ACHIEVEMENT[type_val]}{formatted_target}{TYPE_UNITS_ACHIEVEMENT[type_val]}。"
|
||||||
|
else:
|
||||||
|
if type_val not in TYPE_DESCRIPTIONS:
|
||||||
|
return True
|
||||||
|
formatted_target = format_number(target)
|
||||||
|
expected_desc = f"{TYPE_DESCRIPTIONS[type_val]}{formatted_target}{TYPE_UNITS[type_val]}。"
|
||||||
|
|
||||||
|
if desc != expected_desc:
|
||||||
|
self._add_error(row_num, "desc", "逻辑错误", f"desc与type/target不匹配,期望: '{expected_desc}',实际: '{desc}'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_type(self, row_num, type_val, category):
|
||||||
|
if category == 9:
|
||||||
|
if type_val < 1 or type_val > 23:
|
||||||
|
self._add_error(row_num, "type", "枚举错误", f"category=9(成就)时type值{type_val}不在允许范围1-23内")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if type_val < 1 or type_val > 17:
|
||||||
|
self._add_error(row_num, "type", "枚举错误", f"category≠9时type值{type_val}不在允许范围1-17内")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_target(self, row_num, target):
|
||||||
|
if target < 1:
|
||||||
|
self._add_error(row_num, "target", "范围错误", f"target必须为正整数,当前值: {target}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_openUI_type(self, row_num, openUI_type, type_val):
|
||||||
|
if openUI_type < 1 or openUI_type > 10:
|
||||||
|
self._add_error(row_num, "openUI_type", "枚举错误", f"openUI_type值{openUI_type}不在允许范围1-10内")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if type_val in TYPE_TO_OPENUI:
|
||||||
|
expected = TYPE_TO_OPENUI[type_val]
|
||||||
|
if openUI_type != expected:
|
||||||
|
self._add_error(row_num, "openUI_type", "映射错误", f"type={type_val}时openUI_type应为{expected},当前值: {openUI_type}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_openUI_desc(self, row_num, openUI_desc, openUI_type):
|
||||||
|
if openUI_type in OPENUI_TO_DESC:
|
||||||
|
expected = OPENUI_TO_DESC[openUI_type]
|
||||||
|
if self._parse_str(openUI_desc) != expected:
|
||||||
|
self._add_error(row_num, "openUI_desc", "映射错误", f"openUI_type={openUI_type}时openUI_desc应为'{expected}',当前值: '{openUI_desc}'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_score(self, row_num, score, category):
|
||||||
|
expected = CATEGORY_SCORE_MAP.get(category, 0)
|
||||||
|
if score != expected:
|
||||||
|
self._add_error(row_num, "score", "计算错误", f"category={category}({CATEGORY_DESCRIPTIONS.get(category,'未知')})时score应为{expected},当前值: {score}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_auto(self, row_num, auto):
|
||||||
|
if auto != 0:
|
||||||
|
self._add_error(row_num, "auto", "固定值错误", f"auto必须为0,当前值: {auto}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_extra(self, row_num, extra):
|
||||||
|
extra_str = self._parse_str(extra).lower()
|
||||||
|
if extra_str != "" and extra_str != "null":
|
||||||
|
self._add_error(row_num, "extra", "固定值错误", f"extra必须为null,当前值: {extra}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_id_continuity(self):
|
||||||
|
for category, ids in self.category_ids.items():
|
||||||
|
sorted_ids = sorted(ids)
|
||||||
|
expected_base = category * 10000
|
||||||
|
|
||||||
|
for i, id_val in enumerate(sorted_ids):
|
||||||
|
expected = expected_base + (i + 1)
|
||||||
|
if id_val != expected:
|
||||||
|
row_num = self.id_to_row[id_val]
|
||||||
|
self._add_error(row_num, "id", "连续性错误", f"category={category}的id不连续,期望: {expected},实际: {id_val}")
|
||||||
|
|
||||||
|
def validate_row(self, row_num, row):
|
||||||
|
errors_before = len(self.errors)
|
||||||
|
|
||||||
|
id_val = self._parse_int(row.get('id'))
|
||||||
|
category = self._parse_int(row.get('category'))
|
||||||
|
next_val = self._parse_int(row.get('next'), 0)
|
||||||
|
desc = row.get('desc')
|
||||||
|
type_val = self._parse_int(row.get('type'))
|
||||||
|
target = self._parse_int(row.get('target'))
|
||||||
|
openUI_type = self._parse_int(row.get('openUI_type'))
|
||||||
|
openUI_desc = row.get('openUI_desc')
|
||||||
|
score = self._parse_int(row.get('score'), 0)
|
||||||
|
auto = self._parse_int(row.get('auto'), 0)
|
||||||
|
extra = row.get('extra')
|
||||||
|
|
||||||
|
if id_val is None:
|
||||||
|
self._add_error(row_num, "id", "空值错误", "id字段不能为空")
|
||||||
|
return
|
||||||
|
|
||||||
|
if category is None:
|
||||||
|
self._add_error(row_num, "category", "空值错误", "category字段不能为空")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.validate_id(row_num, id_val, category)
|
||||||
|
self.validate_category(row_num, category)
|
||||||
|
self.validate_type(row_num, type_val, category)
|
||||||
|
|
||||||
|
if type_val is not None:
|
||||||
|
if (category == 9 and type_val >= 1 and type_val <= 23) or (category != 9 and type_val >= 1 and type_val <= 17):
|
||||||
|
if target is None:
|
||||||
|
self._add_error(row_num, "target", "空值错误", "target字段不能为空")
|
||||||
|
else:
|
||||||
|
self.validate_target(row_num, target)
|
||||||
|
self.validate_desc(row_num, desc, type_val, target, category)
|
||||||
|
|
||||||
|
if openUI_type is not None:
|
||||||
|
self.validate_openUI_type(row_num, openUI_type, type_val)
|
||||||
|
self.validate_openUI_desc(row_num, openUI_desc, openUI_type)
|
||||||
|
|
||||||
|
self.validate_next(row_num, next_val, category)
|
||||||
|
self.validate_score(row_num, score, category)
|
||||||
|
self.validate_auto(row_num, auto)
|
||||||
|
self.validate_extra(row_num, extra)
|
||||||
|
|
||||||
|
return len(self.errors) == errors_before
|
||||||
|
|
||||||
|
def validate_file(self, file_path):
|
||||||
|
self.errors = []
|
||||||
|
self.all_ids = set()
|
||||||
|
self.id_to_row = {}
|
||||||
|
self.category_ids = defaultdict(list)
|
||||||
|
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
if len(lines) < 3:
|
||||||
|
return self.errors
|
||||||
|
|
||||||
|
fieldnames = lines[2].strip().split(',')
|
||||||
|
|
||||||
|
data_lines = lines[3:]
|
||||||
|
reader = csv.DictReader(data_lines, fieldnames=fieldnames)
|
||||||
|
row_num = 4
|
||||||
|
|
||||||
|
all_rows = []
|
||||||
|
for row in reader:
|
||||||
|
all_rows.append((row_num, row))
|
||||||
|
|
||||||
|
id_val = self._parse_int(row.get('id'))
|
||||||
|
category = self._parse_int(row.get('category'))
|
||||||
|
|
||||||
|
if id_val is not None and category is not None:
|
||||||
|
self.all_ids.add(id_val)
|
||||||
|
self.category_ids[category].append(id_val)
|
||||||
|
self.id_to_row[id_val] = row_num
|
||||||
|
|
||||||
|
row_num += 1
|
||||||
|
|
||||||
|
for row_num, row in all_rows:
|
||||||
|
self.validate_row(row_num, row)
|
||||||
|
|
||||||
|
self.validate_id_continuity()
|
||||||
|
|
||||||
|
return self.errors
|
||||||
|
|
||||||
|
def generate_report(self, file_path):
|
||||||
|
errors = self.validate_file(file_path)
|
||||||
|
|
||||||
|
report = []
|
||||||
|
report.append("=" * 80)
|
||||||
|
report.append("Quest 表数据校验报告")
|
||||||
|
report.append("=" * 80)
|
||||||
|
report.append(f"校验文件: {file_path}")
|
||||||
|
report.append(f"校验时间: {self._get_timestamp()}")
|
||||||
|
report.append("")
|
||||||
|
|
||||||
|
if not errors:
|
||||||
|
report.append("[OK] 所有数据校验通过")
|
||||||
|
report.append("")
|
||||||
|
report.append("校验统计:")
|
||||||
|
report.append(f" - 校验记录数: {len(self.all_ids)}")
|
||||||
|
report.append(f" - 错误数: 0")
|
||||||
|
for category, ids in sorted(self.category_ids.items()):
|
||||||
|
report.append(f" - {CATEGORY_DESCRIPTIONS.get(category, f'category={category}')}: {len(ids)} 条")
|
||||||
|
else:
|
||||||
|
report.append(f"[FAIL] 发现 {len(errors)} 个错误")
|
||||||
|
report.append("")
|
||||||
|
|
||||||
|
error_by_type = defaultdict(list)
|
||||||
|
for error in errors:
|
||||||
|
error_by_type[error.error_type].append(error)
|
||||||
|
|
||||||
|
for error_type, error_list in sorted(error_by_type.items(), key=lambda x: len(x[1]), reverse=True):
|
||||||
|
report.append(f"【{error_type}】({len(error_list)}个)")
|
||||||
|
report.append("-" * 60)
|
||||||
|
for error in error_list[:10]:
|
||||||
|
report.append(f" {error}")
|
||||||
|
if len(error_list) > 10:
|
||||||
|
report.append(f" ... 还有 {len(error_list) - 10} 个错误")
|
||||||
|
report.append("")
|
||||||
|
|
||||||
|
report.append("错误类型统计:")
|
||||||
|
report.append("-" * 60)
|
||||||
|
for error_type, error_list in sorted(error_by_type.items(), key=lambda x: len(x[1]), reverse=True):
|
||||||
|
report.append(f" {error_type}: {len(error_list)} 个")
|
||||||
|
|
||||||
|
report.append("=" * 80)
|
||||||
|
|
||||||
|
return "\n".join(report)
|
||||||
|
|
||||||
|
def _get_timestamp(self):
|
||||||
|
from datetime import datetime
|
||||||
|
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Quest表数据校验工具')
|
||||||
|
parser.add_argument('-f', '--file', required=True, help='要校验的CSV文件路径')
|
||||||
|
parser.add_argument('-o', '--output', help='校验报告输出文件路径')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
validator = QuestValidator()
|
||||||
|
report = validator.generate_report(args.file)
|
||||||
|
|
||||||
|
print(report)
|
||||||
|
|
||||||
|
if args.output:
|
||||||
|
with open(args.output, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(report)
|
||||||
|
print(f"\n报告已保存至: {args.output}")
|
||||||
|
|
||||||
|
return len(validator.errors)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
exit(main())
|
||||||
209
memories/system_memory.md
Normal file
209
memories/system_memory.md
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# Quest 数据管理系统 - 记忆文件
|
||||||
|
|
||||||
|
## 1. 项目概述
|
||||||
|
|
||||||
|
本项目是一个基于 Markdown 和 CSV 的游戏任务数据管理系统,用于管理游戏中的各种任务数据。
|
||||||
|
|
||||||
|
### 1.1 核心功能
|
||||||
|
|
||||||
|
- **数据建模**: 使用 Markdown 文档定义字段规范和校验规则
|
||||||
|
- **数据存储**: 使用 CSV 文件存储任务数据
|
||||||
|
- **数据校验**: 自动验证数据完整性和格式正确性
|
||||||
|
- **数据索引**: 支持按多种字段进行快速查询
|
||||||
|
|
||||||
|
### 1.2 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
f:\表格/
|
||||||
|
├── quest_schema.md # 数据建模文档
|
||||||
|
├── data_validator.py # 数据校验器
|
||||||
|
├── data_indexer.py # 数据索引系统
|
||||||
|
├── update_main_quest_rewards.py # 主线任务奖励更新脚本
|
||||||
|
├── fix_achievement_desc.py # 成就数据修复脚本
|
||||||
|
├── csv_output/ # CSV 数据文件目录
|
||||||
|
│ └── quest.csv # 任务数据文件
|
||||||
|
└── memories/ # 记忆文件目录
|
||||||
|
└── system_memory.md # 系统记忆文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 当前数据统计
|
||||||
|
|
||||||
|
### 2.1 任务分类统计
|
||||||
|
|
||||||
|
| 分类ID | 分类名称 | 记录数 |
|
||||||
|
| ---- | ----- | --- |
|
||||||
|
| 1 | 主线任务 | 115 |
|
||||||
|
| 2 | 日常任务 | 9 |
|
||||||
|
| 3 | 通行证任务 | 60 |
|
||||||
|
| 4 | 修炼任务 | 15 |
|
||||||
|
| 5 | 周常任务 | 13 |
|
||||||
|
| 6 | 公会任务 | 0 |
|
||||||
|
| 7 | 委托任务 | 0 |
|
||||||
|
| 8 | 称号任务 | 0 |
|
||||||
|
| 9 | 成就任务 | 65 |
|
||||||
|
| 10 | 七日任务 | 0 |
|
||||||
|
|
||||||
|
### 2.2 总计
|
||||||
|
|
||||||
|
- **任务总数**: 277 条
|
||||||
|
- **数据文件**: `csv_output/quest.csv`
|
||||||
|
- **最后更新**: 2026-05-28
|
||||||
|
|
||||||
|
## 3. 核心字段映射
|
||||||
|
|
||||||
|
### 3.1 Category(任务类型)
|
||||||
|
|
||||||
|
| 值 | 描述 | ID格式 |
|
||||||
|
| - | ----- | ----------- |
|
||||||
|
| 1 | 主线任务 | 10001-10115 |
|
||||||
|
| 2 | 日常任务 | 20001-20009 |
|
||||||
|
| 3 | 通行证任务 | 30001-30060 |
|
||||||
|
| 4 | 修炼任务 | 40001-40015 |
|
||||||
|
| 5 | 周常任务 | 50001-50013 |
|
||||||
|
| 9 | 成就任务 | 90001-90065 |
|
||||||
|
|
||||||
|
### 3.2 Type(行为类型)- 普通任务
|
||||||
|
|
||||||
|
| 值 | 描述 | 单位 |
|
||||||
|
| -- | ------ | ----- |
|
||||||
|
| 1 | 招募 | 次 |
|
||||||
|
| 2 | 英雄升级 | 次 |
|
||||||
|
| 3 | 英雄升阶 | 次 |
|
||||||
|
| 4 | 主角升级 | 次 |
|
||||||
|
| 5 | 装备升级 | 次 |
|
||||||
|
| 6 | 通过 | 关主线关卡 |
|
||||||
|
| 7 | 击败 | 波敌人 |
|
||||||
|
| 8 | 商店购买 | 次 |
|
||||||
|
| 9 | 消耗 | 金币 |
|
||||||
|
| 10 | 消耗 | 勾玉 |
|
||||||
|
| 11 | 登录 | 天 |
|
||||||
|
| 12 | 开 | 次宝箱 |
|
||||||
|
| 13 | 挑战 | 次副本 |
|
||||||
|
| 14 | 挑战 | 次竞技场 |
|
||||||
|
| 15 | 获得 | 个英雄 |
|
||||||
|
| 16 | 领取 | 次游历奖励 |
|
||||||
|
| 17 | 世界频道发言 | 次 |
|
||||||
|
|
||||||
|
### 3.3 Type(行为类型)- 成就任务
|
||||||
|
|
||||||
|
| 值 | 描述 | 单位 |
|
||||||
|
| -- | ------- | ----- |
|
||||||
|
| 1 | 累计招募 | 次 |
|
||||||
|
| 2 | 英雄累计升级 | 次 |
|
||||||
|
| 3 | 英雄累计升阶 | 次 |
|
||||||
|
| 4 | 主角累计升级 | 次 |
|
||||||
|
| 5 | 装备累计升级 | 次 |
|
||||||
|
| 6 | 累计通关 | 关主线关卡 |
|
||||||
|
| 7 | 累计通关 | 波敌人 |
|
||||||
|
| 8 | 商店累计消费 | 次 |
|
||||||
|
| 9 | 累计消耗 | 金币 |
|
||||||
|
| 10 | 累计消耗 | 钻石 |
|
||||||
|
| 11 | 累计登录 | 天 |
|
||||||
|
| 12 | 无 | - |
|
||||||
|
| 13 | 累计挑战副本 | 次 |
|
||||||
|
| 14 | 累计挑战竞技场 | 次 |
|
||||||
|
| 15 | 累计获得 | 位英雄 |
|
||||||
|
| 16 | 累计领取 | 次挂机奖励 |
|
||||||
|
| 17 | 累计消耗 | 秒 |
|
||||||
|
| 18 | 累计点赞好友 | 次 |
|
||||||
|
| 19 | 累计英雄委托 | 次 |
|
||||||
|
| 20 | 累计击败 | 个敌人 |
|
||||||
|
| 21 | 战力达到 | 万 |
|
||||||
|
| 22 | 主角到达 | 阶 |
|
||||||
|
| 23 | 累计委托 | 次 |
|
||||||
|
|
||||||
|
## 4. 奖励系统配置
|
||||||
|
|
||||||
|
### 4.1 主线任务奖励增长规则
|
||||||
|
|
||||||
|
| 奖励ID | 初始值 | 增长机制 | 最后值(预测) |
|
||||||
|
| -------------- | ---- | ------------ | ------- |
|
||||||
|
| item\_10000001 | 2400 | 随机增长 400-450 | \~50000 |
|
||||||
|
| item\_10000002 | 50 | 固定增长 50 | 5750 |
|
||||||
|
|
||||||
|
### 4.2 Score(任务积分)映射
|
||||||
|
|
||||||
|
| Category | Score |
|
||||||
|
| -------- | ----- |
|
||||||
|
| 2(日常) | 20 |
|
||||||
|
| 5(周常) | 20 |
|
||||||
|
| 6(公会) | 20 |
|
||||||
|
| 10(七日) | 20 |
|
||||||
|
| 9(成就) | 25 |
|
||||||
|
| 其他 | 0 |
|
||||||
|
|
||||||
|
## 5. 系统规则
|
||||||
|
|
||||||
|
### 5.1 ID 生成规则
|
||||||
|
|
||||||
|
- 格式: `category * 10000 + 序号`
|
||||||
|
- 序号从 0001 开始连续递增
|
||||||
|
- 示例: 10001, 10002, 10003...
|
||||||
|
|
||||||
|
### 5.2 Next 字段规则
|
||||||
|
|
||||||
|
- category=1(主线): 可填写后续任务ID或0
|
||||||
|
- category≠1: 必须填写0
|
||||||
|
|
||||||
|
### 5.3 Desc 字段规则
|
||||||
|
|
||||||
|
- 必须以中文句号"。"结尾
|
||||||
|
- 格式: `类型描述 + 数值(格式化) + 单位 + 。`
|
||||||
|
- 数值 >= 1亿: X亿
|
||||||
|
- 数值 >= 1万: X万
|
||||||
|
- 数值 < 1万: 原值
|
||||||
|
|
||||||
|
## 6. 操作历史
|
||||||
|
|
||||||
|
### 6.1 最近操作记录
|
||||||
|
|
||||||
|
| 时间 | 操作 | 描述 |
|
||||||
|
| ---------- | ------ | ----------------------- |
|
||||||
|
| 2026-05-14 | 奖励配置更新 | 更新主线任务奖励增长机制 |
|
||||||
|
| 2026-05-14 | 成就规则更新 | 添加category=9的特殊type映射规则 |
|
||||||
|
| 2026-05-14 | 数据修复 | 修复65条成就数据的desc字段 |
|
||||||
|
| 2026-05-14 | 字段规范更新 | 添加score字段取值规则 |
|
||||||
|
| 2026-05-14 | 字段规范更新 | 添加next字段填写规则 |
|
||||||
|
|
||||||
|
### 6.2 脚本工具列表
|
||||||
|
|
||||||
|
| 脚本名称 | 功能 |
|
||||||
|
| ------------------------------ | -------- |
|
||||||
|
| `data_validator.py` | 数据校验工具 |
|
||||||
|
| `data_indexer.py` | 数据索引工具 |
|
||||||
|
| `update_main_quest_rewards.py` | 主线任务奖励更新 |
|
||||||
|
| `fix_achievement_desc.py` | 成就desc修复 |
|
||||||
|
|
||||||
|
## 7. 数据索引
|
||||||
|
|
||||||
|
### 7.1 索引字段
|
||||||
|
|
||||||
|
- **idx\_id**: 主键索引(任务唯一标识)
|
||||||
|
- **idx\_category**: 分类索引(按任务类型检索)
|
||||||
|
- **idx\_type**: 类型索引(按行为类型检索)
|
||||||
|
|
||||||
|
### 7.2 查询示例
|
||||||
|
|
||||||
|
- 按ID查询: `SELECT * FROM quest WHERE id = ?`
|
||||||
|
- 按分类查询: `SELECT * FROM quest WHERE category = ?`
|
||||||
|
- 按类型查询: `SELECT * FROM quest WHERE type = ?`
|
||||||
|
|
||||||
|
## 8. 扩展计划
|
||||||
|
|
||||||
|
### 8.1 待扩展表格
|
||||||
|
|
||||||
|
| 表格名 | 用途 | 优先级 |
|
||||||
|
| ---------------- | ------- | --- |
|
||||||
|
| quest\_progress | 玩家任务进度 | 高 |
|
||||||
|
| quest\_group | 任务组/任务链 | 中 |
|
||||||
|
| quest\_condition | 任务解锁条件 | 中 |
|
||||||
|
|
||||||
|
### 8.2 未来扩展字段
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 用途 |
|
||||||
|
| ----------------- | -------- | ------- |
|
||||||
|
| start\_time | datetime | 任务开始时间 |
|
||||||
|
| end\_time | datetime | 任务结束时间 |
|
||||||
|
| repeat\_type | int | 重复类型 |
|
||||||
|
| unlock\_condition | string | 解锁条件表达式 |
|
||||||
|
|
||||||
351
quest_schema.md
Normal file
351
quest_schema.md
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
# Quest 表数据建模文档
|
||||||
|
|
||||||
|
## 1. 表概述
|
||||||
|
|
||||||
|
`quest` 表用于存储游戏中的任务数据,包括主线任务、日常任务、周常任务、成就等多种任务类型。
|
||||||
|
|
||||||
|
## 2. 字段定义
|
||||||
|
|
||||||
|
### 2.1 字段列表
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|
||||||
|
|--------|------|------|--------|------|
|
||||||
|
| id | int | 是 | - | 任务唯一标识 |
|
||||||
|
| category | int | 是 | - | 任务类型 |
|
||||||
|
| next | int | 否 | 0 | 下一个任务ID |
|
||||||
|
| desc | string | 是 | - | 任务描述 |
|
||||||
|
| type | int | 是 | - | 任务行为类型 |
|
||||||
|
| target | int | 是 | - | 任务目标值 |
|
||||||
|
| openUI_type | int | 是 | - | 界面跳转类型 |
|
||||||
|
| openUI_desc | string | 是 | - | 界面跳转描述 |
|
||||||
|
| rewards | string[] | 否 | - | 任务奖励 |
|
||||||
|
| score | int | 是 | 0 | 任务积分 |
|
||||||
|
| auto | int | 是 | 0 | 自动领奖标识 |
|
||||||
|
| extra | string | 否 | null | 额外数据 |
|
||||||
|
|
||||||
|
### 2.2 字段详细定义
|
||||||
|
|
||||||
|
#### 2.2.1 id 字段
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 必须唯一,不允许重复
|
||||||
|
|
||||||
|
**构成规则**: `category类型数字 + 4位序号(从0001开始)`
|
||||||
|
|
||||||
|
**格式示例**:
|
||||||
|
- category=1(主线) → 10001, 10002, 10003...
|
||||||
|
- category=2(日常) → 20001, 20002, 20003...
|
||||||
|
- category=9(成就) → 90001, 90002, 90003...
|
||||||
|
|
||||||
|
**校验规则**:
|
||||||
|
- 必须为5位数字
|
||||||
|
- 第一位必须与category字段匹配
|
||||||
|
- 后四位必须从0001开始连续递增
|
||||||
|
|
||||||
|
#### 2.2.2 category 字段(任务类型)
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 枚举值
|
||||||
|
|
||||||
|
**允许值**:
|
||||||
|
|
||||||
|
| 值 | 描述 |
|
||||||
|
|----|------|
|
||||||
|
| 1 | 主线任务 |
|
||||||
|
| 2 | 日常任务 |
|
||||||
|
| 3 | 通行证任务 |
|
||||||
|
| 4 | 修炼任务 |
|
||||||
|
| 5 | 周常任务 |
|
||||||
|
| 6 | 公会任务 |
|
||||||
|
| 7 | 委托任务 |
|
||||||
|
| 8 | 称号任务 |
|
||||||
|
| 9 | 成就任务 |
|
||||||
|
| 10 | 七日任务 |
|
||||||
|
|
||||||
|
#### 2.2.3 next 字段
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 非负整数,根据category字段决定
|
||||||
|
|
||||||
|
**填写规则**:
|
||||||
|
1. 当category=1(主线任务)时:
|
||||||
|
- 如果存在后续任务,填写下一个任务的ID
|
||||||
|
- 如果不存在后续任务(当前为流程终点),填写0
|
||||||
|
2. 当category≠1时:
|
||||||
|
- 无论是否存在后续任务,统一填写0
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 表示当前任务完成后解锁的下一个任务ID
|
||||||
|
- 值为0表示没有后续任务或当前任务分类不需要链式任务
|
||||||
|
|
||||||
|
#### 2.2.4 desc 字段(任务描述)
|
||||||
|
|
||||||
|
**类型**: string
|
||||||
|
**约束**: 必须与type和target字段关联
|
||||||
|
|
||||||
|
**生成规则**: `type字段对应的中文描述 + target字段的数值(格式化后)+ 单位`
|
||||||
|
|
||||||
|
**数值单位转换规则**:
|
||||||
|
- 当数值 >= 100000000(1亿)时,转换为"亿"为单位,保留整数(例如:500000000 → "5亿")
|
||||||
|
- 当数值 >= 10000(1万)且 < 100000000(1亿)时,转换为"万"为单位,保留整数(例如:9000000 → "900万")
|
||||||
|
- 当数值 < 10000(1万)时,保持原数值(例如:1000 → "1000")
|
||||||
|
|
||||||
|
**文本格式规范**:
|
||||||
|
- 所有描述文本必须以中文句号"。"结尾
|
||||||
|
- 不得使用英文句号"."或其他符号结尾
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
- type=9(消耗X金币), target=1000 → "消耗1000金币。"
|
||||||
|
- type=9(消耗X金币), target=9000000 → "消耗900万金币。"
|
||||||
|
- type=9(消耗X金币), target=500000000 → "消耗5亿金币。"
|
||||||
|
- type=1(招募X次), target=5 → "招募5次。"
|
||||||
|
|
||||||
|
**校验规则**:
|
||||||
|
- desc字段必须能通过type和target字段反向推导验证
|
||||||
|
- 必须符合数值单位转换规则
|
||||||
|
- 必须以中文句号"。"结尾
|
||||||
|
|
||||||
|
#### 2.2.5 type 字段(任务行为类型)
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 枚举值,根据category字段决定取值范围
|
||||||
|
|
||||||
|
**允许值及对应描述(category≠9时)**:
|
||||||
|
|
||||||
|
| 值 | 描述 |
|
||||||
|
|----|------|
|
||||||
|
| 1 | 招募X次 |
|
||||||
|
| 2 | 英雄升级X次 |
|
||||||
|
| 3 | 英雄升阶X次 |
|
||||||
|
| 4 | 主角升级X次 |
|
||||||
|
| 5 | 装备升级X次 |
|
||||||
|
| 6 | 通过X关主线关卡 |
|
||||||
|
| 7 | 通过X波敌人 |
|
||||||
|
| 8 | 商店消费X次 |
|
||||||
|
| 9 | 消耗X金币 |
|
||||||
|
| 10 | 消耗X钻石 |
|
||||||
|
| 11 | 登录X天 |
|
||||||
|
| 12 | 开X次宝箱 |
|
||||||
|
| 13 | 挑战X次副本 |
|
||||||
|
| 14 | 挑战X次竞技场 |
|
||||||
|
| 15 | 获得X个英雄 |
|
||||||
|
| 16 | 领取X次游历奖励 |
|
||||||
|
| 17 | 世界频道发言X次 |
|
||||||
|
|
||||||
|
**允许值及对应描述(category=9成就任务时)**:
|
||||||
|
|
||||||
|
| 值 | 描述 |
|
||||||
|
|----|------|
|
||||||
|
| 1 | 累计招募X次 |
|
||||||
|
| 2 | 英雄累计升级X次 |
|
||||||
|
| 3 | 英雄累计升阶X次 |
|
||||||
|
| 4 | 主角累计升级X次 |
|
||||||
|
| 5 | 装备累计升级X次 |
|
||||||
|
| 6 | 累计通关X关主线关卡 |
|
||||||
|
| 7 | 累计通关X波敌人 |
|
||||||
|
| 8 | 商店累计消费X次 |
|
||||||
|
| 9 | 累计消耗X金币 |
|
||||||
|
| 10 | 累计消耗X钻石 |
|
||||||
|
| 11 | 累计登录X天 |
|
||||||
|
| 12 | 无X |
|
||||||
|
| 13 | 累计挑战副本X次 |
|
||||||
|
| 14 | 累计挑战竞技场X次 |
|
||||||
|
| 15 | 累计获得X位英雄 |
|
||||||
|
| 16 | 累计领取X次挂机奖励 |
|
||||||
|
| 17 | 累计消耗X秒 |
|
||||||
|
| 18 | 累计点赞好友X次 |
|
||||||
|
| 19 | 累计英雄委托X次 |
|
||||||
|
| 20 | 累计击败X个敌人 |
|
||||||
|
| 21 | 战力达到X万 |
|
||||||
|
| 22 | 主角到达X阶 |
|
||||||
|
| 23 | 累计委托X次 |
|
||||||
|
|
||||||
|
#### 2.2.6 target 字段(任务目标值)
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 正整数
|
||||||
|
|
||||||
|
**说明**: 存储任务目标的具体数值,如"招募5次"中的5
|
||||||
|
|
||||||
|
#### 2.2.7 openUI_type 字段(界面跳转类型)
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 枚举值,由type字段决定
|
||||||
|
|
||||||
|
**允许值及对应描述**:
|
||||||
|
|
||||||
|
| 值 | 描述 |
|
||||||
|
|----|------|
|
||||||
|
| 1 | UI_MainPanel |
|
||||||
|
| 2 | UI_DrawCardPanel |
|
||||||
|
| 3 | UI_ShopPanel |
|
||||||
|
| 4 | UI_FBPanel |
|
||||||
|
| 5 | UI_IdlePanel |
|
||||||
|
| 6 | UI_ChatPanel |
|
||||||
|
| 7 | UI_EquipPanel |
|
||||||
|
| 8 | UI_HeroPanel |
|
||||||
|
| 9 | UI_FriendPanel |
|
||||||
|
| 10 | UI_ArePanel |
|
||||||
|
|
||||||
|
**type与openUI_type映射关系**:
|
||||||
|
|
||||||
|
| type | openUI_type | openUI_desc |
|
||||||
|
|------|-------------|-------------|
|
||||||
|
| 1 | 2 | UI_DrawCardPanel |
|
||||||
|
| 2 | 8 | UI_HeroPanel |
|
||||||
|
| 3 | 8 | UI_HeroPanel |
|
||||||
|
| 4 | 8 | UI_HeroPanel |
|
||||||
|
| 5 | 7 | UI_EquipPanel |
|
||||||
|
| 6 | 1 | UI_MainPanel |
|
||||||
|
| 7 | 1 | UI_MainPanel |
|
||||||
|
| 8 | 3 | UI_ShopPanel |
|
||||||
|
| 9 | 3 | UI_ShopPanel |
|
||||||
|
| 10 | 3 | UI_ShopPanel |
|
||||||
|
| 11 | 1 | UI_MainPanel |
|
||||||
|
| 12 | 1 | UI_MainPanel |
|
||||||
|
| 13 | 4 | UI_FBPanel |
|
||||||
|
| 14 | 10 | UI_ArePanel |
|
||||||
|
| 15 | 2 | UI_DrawCardPanel |
|
||||||
|
| 16 | 5 | UI_IdlePanel |
|
||||||
|
| 17 | 6 | UI_ChatPanel |
|
||||||
|
|
||||||
|
#### 2.2.8 openUI_desc 字段(界面跳转描述)
|
||||||
|
|
||||||
|
**类型**: string
|
||||||
|
**约束**: 必须与openUI_type字段严格对应
|
||||||
|
|
||||||
|
**对应关系**:
|
||||||
|
|
||||||
|
| openUI_type | openUI_desc |
|
||||||
|
|-------------|-------------|
|
||||||
|
| 1 | UI_MainPanel |
|
||||||
|
| 2 | UI_DrawCardPanel |
|
||||||
|
| 3 | UI_ShopPanel |
|
||||||
|
| 4 | UI_FBPanel |
|
||||||
|
| 5 | UI_IdlePanel |
|
||||||
|
| 6 | UI_ChatPanel |
|
||||||
|
| 7 | UI_EquipPanel |
|
||||||
|
| 8 | UI_HeroPanel |
|
||||||
|
| 9 | UI_FriendPanel |
|
||||||
|
| 10 | UI_ArePanel |
|
||||||
|
|
||||||
|
#### 2.2.9 rewards 字段(任务奖励)
|
||||||
|
|
||||||
|
**类型**: string[]
|
||||||
|
**格式**: `item_<物品ID>_<数量>,item_<物品ID>_<数量>,...`
|
||||||
|
|
||||||
|
**示例**: `item_10000001_2400,item_10000002_50`
|
||||||
|
|
||||||
|
#### 2.2.10 score 字段(任务积分)
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 根据category字段计算
|
||||||
|
|
||||||
|
**取值规则**:
|
||||||
|
|
||||||
|
| category | score |
|
||||||
|
|----------|-------|
|
||||||
|
| 2(日常) | 20 |
|
||||||
|
| 5(周常) | 20 |
|
||||||
|
| 6(公会) | 20 |
|
||||||
|
| 10(七日任务) | 20 |
|
||||||
|
| 9(成就) | 25 |
|
||||||
|
| 其他 | 0 |
|
||||||
|
|
||||||
|
#### 2.2.11 auto 字段
|
||||||
|
|
||||||
|
**类型**: int
|
||||||
|
**约束**: 固定值
|
||||||
|
|
||||||
|
**允许值**:
|
||||||
|
- 0: 否(手动领奖)
|
||||||
|
|
||||||
|
#### 2.2.12 extra 字段
|
||||||
|
|
||||||
|
**类型**: string
|
||||||
|
**约束**: 固定值
|
||||||
|
|
||||||
|
**允许值**:
|
||||||
|
- null: 无额外数据
|
||||||
|
|
||||||
|
## 3. 数据校验规则
|
||||||
|
|
||||||
|
### 3.1 字段级校验
|
||||||
|
|
||||||
|
| 字段 | 校验规则 | 错误类型 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| id | 必须为5位数字,第一位与category匹配,后四位从0001开始 | 格式错误/重复/不连续 |
|
||||||
|
| category | 必须在1-10范围内 | 枚举值错误 |
|
||||||
|
| next | 必须为非负整数,若不为0则必须存在对应id | 范围错误/引用错误 |
|
||||||
|
| desc | 必须符合type+target的组合规则(category=9时使用成就映射) | 格式错误/逻辑错误 |
|
||||||
|
| type | category=9时必须在1-23范围内;其他category必须在1-17范围内 | 枚举值错误 |
|
||||||
|
| target | 必须为正整数 | 范围错误 |
|
||||||
|
| openUI_type | 必须与type字段正确映射 | 映射错误 |
|
||||||
|
| openUI_desc | 必须与openUI_type字段正确对应 | 映射错误 |
|
||||||
|
| score | 必须根据category字段正确计算 | 计算错误 |
|
||||||
|
| auto | 必须为0 | 固定值错误 |
|
||||||
|
| extra | 必须为null | 固定值错误 |
|
||||||
|
|
||||||
|
### 3.2 记录级校验
|
||||||
|
|
||||||
|
1. **id唯一性**: 所有记录的id字段必须唯一
|
||||||
|
2. **外键引用**: next字段引用的id必须存在
|
||||||
|
3. **逻辑一致性**: desc、type、target三者必须保持逻辑一致
|
||||||
|
4. **界面跳转一致性**: openUI_type、openUI_desc、type三者必须保持一致
|
||||||
|
|
||||||
|
## 4. 数据索引设计
|
||||||
|
|
||||||
|
### 4.1 索引字段
|
||||||
|
|
||||||
|
| 索引名 | 字段 | 类型 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| idx_id | id | 主键 | 唯一标识,快速查询 |
|
||||||
|
| idx_category | category | 普通索引 | 按任务类型检索 |
|
||||||
|
| idx_type | type | 普通索引 | 按行为类型检索 |
|
||||||
|
|
||||||
|
### 4.2 检索方式
|
||||||
|
|
||||||
|
- **按id查询**: `SELECT * FROM quest WHERE id = ?`
|
||||||
|
- **按category查询**: `SELECT * FROM quest WHERE category = ?`
|
||||||
|
- **按type查询**: `SELECT * FROM quest WHERE type = ?`
|
||||||
|
- **组合查询**: `SELECT * FROM quest WHERE category = ? AND type = ?`
|
||||||
|
|
||||||
|
## 5. 数据导入导出规范
|
||||||
|
|
||||||
|
### 5.1 CSV文件格式
|
||||||
|
|
||||||
|
- **编码**: UTF-8
|
||||||
|
- **分隔符**: 逗号(,)
|
||||||
|
- **文件位置**: `csv_output/quest.csv`
|
||||||
|
- **首行**: 字段名(英文)
|
||||||
|
- **第二行**: 字段类型
|
||||||
|
- **第三行**: 字段说明
|
||||||
|
- **数据行**: 从第四行开始
|
||||||
|
|
||||||
|
### 5.2 格式示例
|
||||||
|
|
||||||
|
```csv
|
||||||
|
id,category,next,desc,type,target,openUI_type,openUI_desc,rewards,score,auto,extra
|
||||||
|
int,int,int,string,int,int,int,string,string[],int,int,string
|
||||||
|
主键,分类,下一个任务,描述,子类型,任务目标,打开界面类型,打开界面描述,奖励,每日任务活跃度,自动领奖,额外数据
|
||||||
|
10001,1,10002,通过1关主线关卡,6,1,1,UI_MainPanel,"item_10000001_2400,item_10000002_50",0,0,null
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 扩展计划
|
||||||
|
|
||||||
|
### 6.1 待扩展表格
|
||||||
|
|
||||||
|
| 表格名 | 用途 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| quest_progress | 玩家任务进度 | 高 |
|
||||||
|
| quest_group | 任务组/任务链 | 中 |
|
||||||
|
| quest_condition | 任务解锁条件 | 中 |
|
||||||
|
|
||||||
|
### 6.2 未来扩展字段
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 用途 |
|
||||||
|
|--------|------|------|
|
||||||
|
| start_time | datetime | 任务开始时间 |
|
||||||
|
| end_time | datetime | 任务结束时间 |
|
||||||
|
| repeat_type | int | 重复类型(每日/每周/一次性) |
|
||||||
|
| unlock_condition | string | 解锁条件表达式 |
|
||||||
101
update_main_quest_rewards.py
Normal file
101
update_main_quest_rewards.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import csv
|
||||||
|
import random
|
||||||
|
|
||||||
|
def update_main_quest_rewards(input_path, output_path=None):
|
||||||
|
if output_path is None:
|
||||||
|
output_path = input_path
|
||||||
|
|
||||||
|
with open(input_path, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
fieldnames = lines[2].strip().split(',')
|
||||||
|
|
||||||
|
data_lines = lines[3:]
|
||||||
|
reader = csv.DictReader(data_lines, fieldnames=fieldnames)
|
||||||
|
|
||||||
|
main_quests = []
|
||||||
|
other_quests = []
|
||||||
|
|
||||||
|
for row in reader:
|
||||||
|
if int(row.get('category', 0)) == 1:
|
||||||
|
main_quests.append(row)
|
||||||
|
else:
|
||||||
|
other_quests.append(row)
|
||||||
|
|
||||||
|
main_quests.sort(key=lambda x: int(x['id']))
|
||||||
|
|
||||||
|
rewards_history = []
|
||||||
|
current_10000001 = 2400
|
||||||
|
current_10000002 = 50
|
||||||
|
|
||||||
|
for i, row in enumerate(main_quests):
|
||||||
|
if i == 0:
|
||||||
|
current_10000001 = 2400
|
||||||
|
current_10000002 = 50
|
||||||
|
else:
|
||||||
|
increase_10000001 = random.randint(400, 450)
|
||||||
|
current_10000001 += increase_10000001
|
||||||
|
current_10000002 += 50
|
||||||
|
|
||||||
|
row['rewards'] = f"item_10000001_{current_10000001},item_10000002_{current_10000002}"
|
||||||
|
|
||||||
|
rewards_history.append({
|
||||||
|
'id': row['id'],
|
||||||
|
'reward_10000001': current_10000001,
|
||||||
|
'reward_10000002': current_10000002
|
||||||
|
})
|
||||||
|
|
||||||
|
all_rows = main_quests + other_quests
|
||||||
|
all_rows.sort(key=lambda x: int(x['id']))
|
||||||
|
|
||||||
|
with open(output_path, 'w', encoding='utf-8', newline='') as f:
|
||||||
|
f.writelines(lines[:3])
|
||||||
|
|
||||||
|
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||||||
|
writer.writerows(all_rows)
|
||||||
|
|
||||||
|
print(f"更新完成,共更新 {len(main_quests)} 条主线任务的奖励配置")
|
||||||
|
print("\n奖励增长预览(前10条):")
|
||||||
|
for i, record in enumerate(rewards_history[:10], 1):
|
||||||
|
print(f" 任务ID {record['id']}: item_10000001={record['reward_10000001']}, item_10000002={record['reward_10000002']}")
|
||||||
|
|
||||||
|
print(f"\n最后一条主线任务奖励:")
|
||||||
|
last_record = rewards_history[-1]
|
||||||
|
print(f" 任务ID {last_record['id']}: item_10000001={last_record['reward_10000001']}, item_10000002={last_record['reward_10000002']}")
|
||||||
|
|
||||||
|
return rewards_history
|
||||||
|
|
||||||
|
def test_reward_calculation():
|
||||||
|
print("\n=== 奖励计算测试 ===")
|
||||||
|
|
||||||
|
initial_1 = 2400
|
||||||
|
initial_2 = 50
|
||||||
|
total_tasks = 115
|
||||||
|
|
||||||
|
test_history = []
|
||||||
|
current_1 = initial_1
|
||||||
|
current_2 = initial_2
|
||||||
|
|
||||||
|
for i in range(total_tasks):
|
||||||
|
if i > 0:
|
||||||
|
increase = random.randint(400, 450)
|
||||||
|
current_1 += increase
|
||||||
|
current_2 += 50
|
||||||
|
test_history.append((current_1, current_2))
|
||||||
|
|
||||||
|
print(f"初始值: item_10000001={initial_1}, item_10000002={initial_2}")
|
||||||
|
print(f"任务总数: {total_tasks}")
|
||||||
|
print(f"第100条任务奖励: item_10000001={test_history[99][0]}, item_10000002={test_history[99][1]}")
|
||||||
|
print(f"最后一条任务奖励: item_10000001={test_history[-1][0]}, item_10000002={test_history[-1][1]}")
|
||||||
|
|
||||||
|
min_increase = 400 * (total_tasks - 1)
|
||||||
|
max_increase = 450 * (total_tasks - 1)
|
||||||
|
print(f"\nitem_10000001理论范围: [{initial_1 + min_increase}, {initial_1 + max_increase}]")
|
||||||
|
print(f"item_10000002理论值: {initial_2 + 50 * (total_tasks - 1)}")
|
||||||
|
|
||||||
|
return test_history
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_reward_calculation()
|
||||||
|
print("\n" + "="*60)
|
||||||
|
update_main_quest_rewards('csv_output/quest.csv')
|
||||||
Reference in New Issue
Block a user