一、需求分析: 

1、 用户注册、登陆功能。

2、 问题描述页面。

3、 两变量决策:准确输入两个变量后,系统给出最优结果和分析过程。

4、 一变量决策:输入一个变量,和另一个变量的范围,系统给出另一个变量在变化过程中的最优结果和折线图。

5、 零变量决策:系统给出在“购买率”分别为3%、4%、5%、6%、7%时,另一个变量在变化过程中的折线图。

6、 每次得出决策结果后,用户可以选择保存结果。在“我的决策记录”中,用户可以查看过去保存的决策记录。

 

二、整体设计

1、 结构:B/S

2、 技术:

Java EE的spring、struts、hibernate框架;

用jfreechart画折线图;

jquery框架;

Css、html、freemarker;

3、 开发工具:

Eclipse、photoshop、firebug

4、 程序规模:(4天*1人)

*.java:1840行

*.js:421行

*.css:1529行

*.ftl:501行

 

三、详细设计和实现  

1、 系统逻辑结构设计:

整个项目采用MVC模式,分层设计提高了系统的易维护性、可扩展性,减少代码的耦合程度。

2、 模块详细设计实现:

(其他略。。。。)

生产折线图的功能:

(1)、客户端ajax提交请求:

1 $('#result')
2 .click(
3 function() {
4 disableSaveButton();
5 var paramName = $.trim($(
6 '#main_box ul li.paramSelected input.radio')
7 .val());
8 var paramValue = $.trim($(
9 '#main_box ul li.paramSelected input.input')
10 .val());
11 var purchaseRate = $.trim($('#purchaseRate').val());
12 var precisionType = $
13 .trim($(
14 '#main_box ul li.precisionSelected input.radio')
15 .val());
16 var minPrecision = $.trim($('#minPrecision').val());
17 var maxPrecision = $.trim($('#maxPrecision').val());
18 var intervalPrecision = $.trim($('#intervalPrecision')
19 .val());
20
21 var decisionUrl = 'one.action?paramName=' + paramName
22 + '&paramValue='
23 + encodeURIComponent(paramValue)
24 + '&precisionType=' + precisionType
25 + '&minPrecision='
26 + encodeURIComponent(minPrecision)
27 + '&maxPrecision='
28 + encodeURIComponent(maxPrecision)
29 + '&intervalPrecision='
30 + encodeURIComponent(intervalPrecision);
31
32 waitLoading();
33 $
34 .ajax({
35 type : 'post',
36 url : 'one.action',
37 data : 'paramName='
38 + paramName
39 + '&paramValue='
40 + encodeURIComponent(paramValue)
41 + '&precisionType='
42 + precisionType
43 + '&minPrecision='
44 + encodeURIComponent(minPrecision)
45 + '&maxPrecision='
46 + encodeURIComponent(maxPrecision)
47 + '&intervalPrecision='
48 + encodeURIComponent(intervalPrecision),
49 error : function() {
50 errorLoading();
51 alert('数据处理失败,请重试!');
52 },
53 success : function(msg) {
54 var json = eval('(' + msg + ')');
55 var success = json['success'];
56 var hitsError = json['hitsError'];
57 var purchaseRateError = json['purchaseRateError'];
58 var scopeError = json['scopeError'];
59 var intervalError = json['intervalError'];
60 var graph = decodeURIComponent(json['graph']);
61 var result = json['result'];
62
63 $(
64 '#main_box ul li.hits div.errorMessage')
65 .html('');
66 $(
67 '#main_box ul li.purchaseRate div.errorMessage')
68 .html('');
69 $(
70 '#main_box ul li.precision ul li div.errorMessage')
71 .html('');
72
73 if (success) {
74 clearLoading();
75 $('#resultString img').attr('src',
76 decodeURIComponent(graph));
77 $('#analysisString .content').html(
78 result);
79 enableSaveButton();
80 $('#saveResult').siblings('input.url').val(decisionUrl);
81 $('#saveResult').siblings('input.hits').val(paramValue);
82 $('#saveResult').siblings('input.purchaseRate').val(paramValue/100);
83 $('#saveResult').siblings('input.paramName').val(paramName);
84 } else {
85 errorLoading();
86 if ('undefined' != (typeof hitsError)
87 && null != hitsError
88 && '' != hitsError) {
89 $(
90 '#main_box ul li.hits div.errorMessage')
91 .html(hitsError);
92 }
93 if ('undefined' != (typeof purchaseRateError)
94 && null != purchaseRateError
95 && '' != purchaseRateError) {
96 $(
97 '#main_box ul li.purchaseRate div.errorMessage')
98 .html(purchaseRateError);
99 }
100 if ('undefined' != (typeof scopeError)
101 && null != scopeError
102 && '' != scopeError) {
103 $(
104 '#main_box ul li.precision ul li.scope div.errorMessage')
105 .html(scopeError);
106 }
107 if ('undefined' != (typeof intervalError)
108 && null != intervalError
109 && '' != intervalError) {
110 $(
111 '#main_box ul li.precision ul li.interval div.errorMessage')
112 .html(intervalError);
113 }
114 }
115 }
116
117 });
118 });

(2)、OneAction类响应ajax请求:(进行数据校验,返回系统生成的决策字符串和折线图的url(例如,chartHits.action?hits=555555555555&min=0.03&max=0.07&interval=0.01))

1 private void initJson() throws JSONException {
2 json = new JSONObject().put("success", success)
3 .put("hitsError", hitsError)
4 .put("purchaseRateError", purchaseRateError)
5 .put("scopeError", scopeError)
6 .put("intervalError", intervalError).put("graph", graph).put("result", result)
7 .toString();
8 }
9
10 public String execute() throws DssException {
11 if (null == paramName
12 || (!paramName.equals("hits") && !paramName
13 .equals("purchaseRate"))) {
14 throw new DssException("*请至少选择一个参数!");
15 }
16
17 if (null == precisionType
18 || (!precisionType.equals("default") && !precisionType
19 .equals("advanced"))) {
20 throw new DssException("*请至少选择一个折线图精确度类型!");
21 }
22 try {
23 if (paramName.equals("hits")) {
24 Long numH;
25
26 Double numMin;
27 Double numMax;
28 Double numInterval;
29
30 if (null == paramValue || paramValue.equals("")) {
31 success = false;
32 hitsError = "*字段不能为空!";
33 initJson();
34 return SUCCESS;
35 }
36
37 String h = URLDecoder.decode(paramValue, "UTF-8");
38 if (!h.matches("\\d+")) {
39 success = false;
40 hitsError = "*字段只能为正整数!";
41 initJson();
42 return SUCCESS;
43 }
44
45 try {
46 numH = Long.parseLong(h);
47 } catch (NumberFormatException e) {
48 success = false;
49 hitsError = "*字段只能为正整数!";
50 initJson();
51 return SUCCESS;
52 }
53 if (numH < 0) {
54 success = false;
55 hitsError = "*字段只能为正整数!";
56 initJson();
57 return SUCCESS;
58 }
59
60 /*
61 * precision validate
62 */
63 if (precisionType.equals("advanced")) {
64 if (null == minPrecision || minPrecision.equals("")
65 || null == maxPrecision || maxPrecision.equals("")) {
66 success = false;
67 scopeError = "*字段不能为空!";
68 initJson();
69 return SUCCESS;
70 }
71 if (null == intervalPrecision
72 || intervalPrecision.equals("")) {
73 success = false;
74 intervalError = "*字段不能为空!";
75 initJson();
76 return SUCCESS;
77 }
78 String min = URLDecoder.decode(minPrecision, "UTF-8");
79 String max = URLDecoder.decode(maxPrecision, "UTF-8");
80 String interval = URLDecoder.decode(intervalPrecision,
81 "UTF-8");
82 if (!min.matches("[\\.\\d]*[\\d]+[\\.\\d]*")
83 || !max.matches("[\\.\\d]*[\\d]+[\\.\\d]*")) {
84 success = false;
85 scopeError = "*字段只能为数字!";
86 initJson();
87 return SUCCESS;
88 }
89 if (!interval.matches("[\\.\\d]*[\\d]+[\\.\\d]*")) {
90 success = false;
91 intervalError = "*字段只能为数字!";
92 initJson();
93 return SUCCESS;
94 }
95
96 try {
97 numMin = Double.parseDouble(min) / 100;
98 numMax = Double.parseDouble(max) / 100;
99 } catch (NumberFormatException e) {
100 success = false;
101 scopeError = "*字段只能为数字!";
102 initJson();
103 return SUCCESS;
104 }
105 try {
106 numInterval = Double.parseDouble(interval) / 100;
107 } catch (NumberFormatException e) {
108 success = false;
109 intervalError = "*字段只能为数字!";
110 initJson();
111 return SUCCESS;
112 }
113 if (numMin < 0 || numMax < 0 || numMin >= numMax) {
114 success = false;
115 scopeError = "*字段只能为正数,并且最大值要大于最小值!";
116 initJson();
117 return SUCCESS;
118 }
119 if (numInterval < 0) {
120 success = false;
121 intervalError = "*字段只能为正数!";
122 initJson();
123 return SUCCESS;
124 }
125
126 } else { // default
127 numMin = 0.03;
128 numMax = 0.07;
129 numInterval = 0.01;
130 }
131
132 // ///
133
134 /*
135 * get graph 点击数:(Long) numH 购买率:(Double) numMin numMax
136 * numInterval
137 */
138 success = true;
139 graph = URLEncoder.encode("chartHits.action?hits=" + numH
140 + "&min=" + numMin + "&max=" + numMax + "&interval="
141 + numInterval, "UTF-8");
142 result = Util.bestSolutions(numH);
143 initJson();
144 return SUCCESS;
145
146 } else {
147 Double numR;
148
149 Long numMax;
150 Long numMin;
151 Long numInterval;
152
153 if (null == paramValue || paramValue.equals("")) {
154 success = false;
155 purchaseRateError = "*字段不能为空!";
156 initJson();
157 return SUCCESS;
158 }
159 String r = URLDecoder.decode(paramValue, "UTF-8");
160 if (!r.matches("[\\.\\d]*[\\d]+[\\.\\d]*")) {
161 success = false;
162 purchaseRateError = "*字段只能为数字!";
163 initJson();
164 return SUCCESS;
165 }
166
167 try {
168 numR = Double.parseDouble(r) / 100;
169 } catch (NumberFormatException e) {
170 success = false;
171 purchaseRateError = "*字段只能为数字!";
172 initJson();
173 return SUCCESS;
174 }
175 if (numR < 0) {
176 success = false;
177 purchaseRateError = "*字段只能为正数!";
178 initJson();
179 return SUCCESS;
180 }
181
182 /*
183 * precision validate
184 */
185 if (precisionType.equals("advanced")) {
186 if (null == minPrecision || minPrecision.equals("")
187 || null == maxPrecision || maxPrecision.equals("")) {
188 success = false;
189 scopeError = "*字段不能为空!";
190 initJson();
191 return SUCCESS;
192 }
193 if (null == intervalPrecision
194 || intervalPrecision.equals("")) {
195 success = false;
196 intervalError = "*字段不能为空!";
197 initJson();
198 return SUCCESS;
199 }
200
201 String min = URLDecoder.decode(minPrecision, "UTF-8");
202 String max = URLDecoder.decode(maxPrecision, "UTF-8");
203 String interval = URLDecoder.decode(intervalPrecision,
204 "UTF-8");
205 if (!min.matches("\\d+") || !max.matches("\\d+")) {
206 success = false;
207 scopeError = "*字段只能为正整数!";
208 initJson();
209 return SUCCESS;
210 }
211 if (!interval.matches("\\d+")) {
212 success = false;
213 intervalError = "*字段只能为正整数!";
214 initJson();
215 return SUCCESS;
216 }
217
218 try {
219 numMax = Long.parseLong(max);
220 numMin = Long.parseLong(min);
221 } catch (NumberFormatException e) {
222 success = false;
223 scopeError = "*字段只能为正整数!";
224 initJson();
225 return SUCCESS;
226 }
227 try {
228 numInterval = Long.parseLong(interval);
229 } catch (NumberFormatException e) {
230 success = false;
231 intervalError = "*字段只能为正整数!";
232 initJson();
233 return SUCCESS;
234 }
235 if (numMax < 0 || numMin < 0 || numMax <= numMin) {
236 success = false;
237 scopeError = "*字段只能为正整数,且最大值大于最小值!";
238 initJson();
239 return SUCCESS;
240 }
241 if (numInterval < 0) {
242 success = false;
243 intervalError = "*字段只能为正整数!";
244 initJson();
245 return SUCCESS;
246 }
247 } else { // default
248 numMin = 450000l;
249 numMax = 800000l;
250 numInterval = 20000l;
251 }
252
253 /*
254 * get graph 购买率:(Double) numR 点击数:(Long) numMin numMax
255 * numInterval
256 */
257 success = true;
258 graph = URLEncoder.encode(
259 "chartPurchaseRate.action?purchaseRate="
260 + URLEncoder.encode(numR.toString(), "UTF-8")
261 + "&min=" + numMin + "&max=" + numMax
262 + "&interval=" + numInterval, "UTF-8");
263 result = Util.bestSolutions(numR);
264 initJson();
265 return SUCCESS;
266
267 }
268 } catch (UnsupportedEncodingException e) {
269 e.printStackTrace();
270 throw new DssException("解码过程出错~");
271 } catch (JSONException e) {
272 e.printStackTrace();
273 throw new DssException("生成json字符串过程出错~");
274 }
275
276 // return SUCCESS;
277 }

(3)ChartHitsAction类负责产生折线图:

1 public String execute() {
2 chart = jFreeChartService.createChartHits(hits, min, max, interval);
3 return SUCCESS;
4 }

配置文件:

1 <action name="chartHits" class="com.dss.action.chart.ChartHitsAction">
2 <result name="success" type="chart">
3 <param name="width">960</param>
4 <param name="height">640</param>
5 </result>
6 </action>

(4)JFreeChartServiceImpl类:

 

1 package com.dss.service.impl;
2
3 import java.awt.Color;
4 import java.awt.Font;
5
6 import org.jfree.chart.ChartFactory;
7 import org.jfree.chart.JFreeChart;
8 import org.jfree.chart.plot.CategoryPlot;
9 import org.jfree.chart.plot.PlotOrientation;
10 import org.jfree.chart.renderer.category.LineAndShapeRenderer;
11 import org.jfree.chart.title.TextTitle;
12 import org.jfree.data.category.DefaultCategoryDataset;
13
14 import com.dss.service.JFreeChartService;
15 import com.dss.service.Util;
16
17 public class JFreeChartServiceImpl implements JFreeChartService {
18 public JFreeChart createChartHits(Long hits, Double min, Double max,
19 Double interval) {
20 DefaultCategoryDataset dcd = createDataset(hits, min, max, interval);
21 JFreeChart chart = ChartFactory.createLineChart("点击量为"+(hits/10000)+"万时,三种销售方案随着购买率变化的折线图。",
22 "购买率(单位:0.01)", "销售收入(单位:10000)", dcd,
23 PlotOrientation.VERTICAL, true, true, false);
24 Font font = new Font("楷体", Font.BOLD, 17);
25
26 TextTitle tt = chart.getTitle();
27 tt.setFont(new Font("隶书", Font.BOLD, 25));
28
29 chart.getLegend().setItemFont(font);
30
31 CategoryPlot plot = chart.getCategoryPlot();
32 plot.getDomainAxis().setTickLabelFont(font);
33 plot.getDomainAxis().setLabelFont(font);
34 plot.getRangeAxis().setLabelFont(font);
35
36 plot.setNoDataMessage("此范围中没有数据!");
37 plot.setNoDataMessageFont(font);
38 plot.setNoDataMessagePaint(Color.RED);
39
40 plot.setBackgroundPaint(Color.BLACK);
41
42 LineAndShapeRenderer lasr = (LineAndShapeRenderer) plot.getRenderer();
43 lasr.setBaseLinesVisible(true);
44 lasr.setBaseItemLabelPaint(Color.BLACK);
45
46 return chart;
47 }
48
49 private DefaultCategoryDataset createDataset(Long hits, Double min,
50 Double max, Double interval) {
51 DefaultCategoryDataset dcd = new DefaultCategoryDataset();
52 for (Double i = min; i <= max; i += interval) {
53 dcd.addValue(Util.solutionOneIncome(hits, i) / 10000, "方案一", i);
54 dcd.addValue(Util.solutionTwoIncome(hits, i) / 10000, "方案二", i);
55 dcd.addValue(Util.solutionThreeIncome(hits, i) / 10000, "方案三", i);
56 }
57 return dcd;
58 }
59
60 private DefaultCategoryDataset createDataset(Double purchaseRate, Long min,
61 Long max, Long interval) {
62 DefaultCategoryDataset dcd = new DefaultCategoryDataset();
63 for (Long i = min; i <= max; i += interval) {
64 dcd.addValue(Util.solutionOneIncome(i, purchaseRate) / 10000,
65 "方案一", Long.valueOf(i / 10000));
66 dcd.addValue(Util.solutionTwoIncome(i, purchaseRate) / 10000,
67 "方案二", Long.valueOf(i / 10000));
68 dcd.addValue(Util.solutionThreeIncome(i, purchaseRate) / 10000,
69 "方案三", Long.valueOf(i / 10000));
70 }
71 return dcd;
72 }
73
74 @Override
75 public JFreeChart createChartHits(Double purchaseRate, Long min, Long max,
76 Long interval) {
77 DefaultCategoryDataset dcd = createDataset(purchaseRate, min, max,
78 interval);
79 JFreeChart chart = ChartFactory.createLineChart("购买率为"+purchaseRate+"时,三种销售方案随点击量变化的折线图。",
80 "点击数(单位:10000)", "销售收入(单位:10000))", dcd,
81 PlotOrientation.VERTICAL, true, true, false);
82 Font font = new Font("楷体", Font.BOLD, 17);
83
84 chart.getLegend().setItemFont(font);
85
86 TextTitle tt = chart.getTitle();
87 tt.setFont(new Font("隶书", Font.BOLD, 25));
88
89 CategoryPlot plot = chart.getCategoryPlot();
90 plot.getDomainAxis().setTickLabelFont(font);
91 plot.getDomainAxis().setLabelFont(font);
92 plot.getRangeAxis().setLabelFont(font);
93
94 plot.setBackgroundPaint(Color.BLACK);
95
96 plot.setNoDataMessage("此范围中没有数据!");
97 plot.setNoDataMessageFont(font);
98 plot.setNoDataMessagePaint(Color.RED);
99
100
101 LineAndShapeRenderer lasr = (LineAndShapeRenderer) plot.getRenderer();
102 lasr.setBaseLinesVisible(true);
103 lasr.setBaseItemLabelPaint(Color.BLACK);
104
105 return chart;
106 }
107 }

 

 

 

 posted on 2011-06-04 19:41  歪步  阅读(907)  评论(0编辑  收藏  举报